c++ - 直接和复制构造函数
Posted
技术标签:
【中文标题】c++ - 直接和复制构造函数【英文标题】:c++ - Direct and Copy Constructors 【发布时间】:2018-05-20 10:52:19 【问题描述】:class UnusualClass
int a;
public:
UnusualClass(int a)std::cout<<"Direct initialization"<<std::endl;
UnusualClass(const UnusualClass &n)std::cout<<"Copy initialization";
;
int main ()
UnusualClass k1(5); //Direct initialization
UnusualClass k2=56; //Copy initialization
return 0;
为什么编译器会打印两次“直接初始化”?我做了一些研究,发现我可能会得到复制构造函数省略。
在这两种情况下是否有可能得到两种不同的结果?
另外,当我使用UnusualClass(const UnusualClass &n)=delete
时,我收到一条错误消息use of deleted function 'UnusualClass::UnusualClass(const UnusualClass&)
。如果它无论如何都跳过这个构造函数,为什么我会得到这个错误?
我知道我可以通过使用两个构造函数 UnusualClass(int a);
和 UnusualClass(double b);
得到两个不同的结果,但这个技巧似乎不太正确。
【问题讨论】:
你为什么期望第二个使用复制构造函数?56
是 int
,而不是 UnusualClass
您似乎对 copy constructor 调用感到困惑 copy-initialization syntax。 copy-initialization 只会在参数是对同一个类的对象的引用时导致 copy constructor 调用。
为第二个提供copy-ctor的要求可能不直观,但省略不是必需的。因此,它必须是可用的,至少通过声明,即使它最终没有被使用。这样想吧。从 56 构造 temporary UnusualClass
会调用直接 ctor。然后复制ctor 将被调用,从临时创建k2
,但是通过省略,一半消失了。
复制构造函数将与UnusualClass k2 = k1;
一起使用
k2的初始化是'隐式'而不是'复制初始化'。
【参考方案1】:
Copy initialization 并不意味着必须调用复制构造函数。
如果 T 是类类型,并且 other 的类型的 cv 非限定版本不是 T 或派生自 T,或者如果 T 是非类类型,但 other 的类型是类类型,用户-检查定义的转换序列,这些转换序列可以从 other 的类型转换为 T(如果 T 是类类型并且转换函数可用,则转换为从 T 派生的类型),并通过重载决议选择最佳的转换序列。如果使用了转换构造函数,则转换的结果是
prvalue temporary (until C++17)
prvalue expression (since C++17)
,然后用于直接初始化对象。The last step is usually optimized out and the result of the conversion is constructed directly in the memory allocated for the target object, but the appropriate constructor (move or copy) is required to be accessible even though it's not used. (until C++17)
在本次拷贝初始化(即UnusualClass k2=56;
)的过程中,会选择UnusualClass::UnusualClass(int)
将int
转换为UnusualClass
,所以首先调用它。之后,转换后的UnusualClass
用于直接初始化对象k2
,因此在概念上需要复制构造函数。在 C++17 甚至 copy elision 发生之前,复制构造函数必须是可访问的,这就是为什么当你使它 delete
编译失败的原因。由于 C++17 copy elision 是有保证的,复制构造函数不需要再次访问。
【讨论】:
【参考方案2】:这不是copy initialization
:
UnusualClass k2=56; // NOT Copy initialization
// for 56 is not of type UnusualClass
它将调用构造函数:
UnusualClass(int a)
我想你的意思是:
UnusualClass k1(5); //Direct initialization
UnusualClass k2k1; //Copy initialization
UnusualClass k2 = k1; //Copy initialization
注意copy initialization
中需要的类型。
UnusualClass(const UnusualClass &n) // const reference to type UnusualClass
对象的类型应该是UnusualClass
,而不是int
更新
我收到一条错误提示使用已删除的功能
UnusualClass::UnusualClass(const UnusualClass&).
如果它无论如何都跳过了这个构造函数,为什么我会得到这个错误?
UnusualClass::UnusualClass(const UnusualClass&) = delete;
意思是:
来自cppreference
避免隐式生成复制构造函数。
因此,您需要定义自己的复制构造函数。
更新 2
更多参考@songyuanyao对copy-initialization
的回答
【讨论】:
OPs 第一段中 last 问题背后的原因很重要。可能值得考虑添加到这个答案中。 @WhozCraig,已更新。谢谢你通知我。也随时帮助我改进我的答案。 我看到了,但它直到没有解释为什么需要它。 OP询问如果从不使用它为什么需要它。简而言之,当它似乎一开始没有被使用时,为什么编译器会抱怨它?请参阅我在 OPs 帖子下方的一般评论。 songyaunyo 的答案实际上是描述这一点的标准。 它是称为复制初始化。查看定义 - en.cppreference.com/w/cpp/language/copy_initialization 谢谢@0x499602D2, @WhozCraig, True @songyuanyao 已经澄清了更多关于copy-initialization
【参考方案3】:
UnusualClass k1(5); //Direct initialization
UnusualClass k2=56; //Copy initialization
在上述两种情况下,您都传递了一个整数,并且唯一带有整数参数的构造函数是
UnusualClass(int a)std::cout<<"Direct initialization"<<std::endl;
因此编译器会打印两次“直接初始化”。
【讨论】:
【参考方案4】:整数 56 与你的构造函数“隐式”相关,它需要一个整数。如果要禁用此行为,可以将构造函数设置为显式。
explicit UnusualClass (int a) ...
禁用意味着这将被检测为编译器错误:
UnusualClass k = 56;
【讨论】:
以上是关于c++ - 直接和复制构造函数的主要内容,如果未能解决你的问题,请参考以下文章