比较 C++ 中的这两种初始化方法以获得一个简单的复数类。
complx one(1,3);
complx two = complx(3,4);
在第二种情况下,我会得到一个构造函数,然后是一个赋值,然后是一个副本,还是只是构造函数?
是否可以区分这两种类型的初始化?
比较 C++ 中的这两种初始化方法以获得一个简单的复数类。
complx one(1,3);
complx two = complx(3,4);
在第二种情况下,我会得到一个构造函数,然后是一个赋值,然后是一个副本,还是只是构造函数?
是否可以区分这两种类型的初始化?
complx two = complx(3,4);
这是一个复制初始化。此规则涵盖了此初始化程序的特定语义:
如果初始化是直接初始化,或者如果是复制初始化,其中源类型的 cv 非限定版本与目标的类 [...] 相同,则考虑构造函数。枚举了适用的构造函数(13.3.1.3),并通过重载决议(13.3)选择最佳构造函数。调用如此选择的构造函数来初始化对象,使用初始化表达式或表达式列表作为其参数。[...]
也就是说,源类型与目标类型相同的复制初始化的行为类似于直接初始化。所以这使得声明等同于:
complx two(complx(3,4));
这会构造一个临时complx
对象,然后使用复制/移动构造函数来构造two
.
但是,可以省略此复制/移动:
当尚未绑定到引用 (12.2) 的临时类对象将被复制/移动到具有相同 cv-unqualified 类型的类对象时,可以通过将临时对象直接构造到目标中来省略复制/移动操作省略的复制/移动
但是,复制构造函数必须仍然可以访问,就好像它正在被调用一样。
可以区分这两种初始化吗?假设复制/移动是有效的,不可靠的,不。如果编译器确实在复制初始化中删除了副本,或者如果副本表现良好并且没有任何额外的副作用,那么两者的行为将完全相同。如果它没有忽略副本并且副本有一些额外的副作用,那么您会注意到不同之处。
首先称为直接初始化。
它只是调用最佳匹配构造函数。
第二种情况称为复制初始化。
它创建一个类型的对象,Complx
然后复制该对象以创建该two
对象。
所以它涉及调用构造函数,然后调用复制构造函数。
编译器通常可以在可能的情况下省略复制构造函数调用(有特殊条件,代码示例满足这些条件)。但是没有任何优化得到保证,它们取决于编译器实现的效率,因此复制构造函数需要可用于第二个示例进行编译。
是否可以区分这两种类型的初始化?
是的,除非复制构造函数可用且可访问,否则第二个示例将无法编译。
好读:
在 C++ 中,您可以通过像这样实现移动构造函数或复制构造函数来区分这两种类型的初始化
struct complx
{
complx(complx && aOther){} // <- move constructor (C++11)
complx(const complx & aOther){} // <- copy constructor (C++03)
complx(float a, float b) {} // <- normal constructor
};
您的第一行代码将调用普通的构造函数。您的第二行代码称为使用右值进行复制初始化,并将调用 C++11 中的移动构造函数和 C++03 中的复制构造函数。请注意,在 C++03 中,complx(complx && aOther){}
无法识别(无效语法),应将其删除。