直接初始化与复制初始化
理解直接初始化与复制初始化的区别
简介
当用于类类型对象时,初始化的复制形式和直接形式有所不同:
- 直接初始化直接调用与实参匹配的构造函数。
- 复制初始化则是首先使用指定构造函数创建一个临时对象,然后用复制构造函数将那个临时对象复制到正在创建的对象。
很多时候编译器会默认给我们进行优化,省去创建临时对象这一步,以此来节省创建临时对象的损耗。
对比
下面的源代码来自于c++拷贝初始化和直接初始化的底层区别
1 |
|
ClassTest ct1(“ab”)
根据参数确定对应的构造函数 : ClassTest(const char* pc);
ClassTest ct2 = “ab”
该构造函数本应该为复制初始化,通过创建”ab”的临时对象,之后将临时对象复制到初始化对象中。
但是结果是显示调用了 ClassTest(const char* pc); 与上面的结果一致。
该结果应是执行了编译器的默认优化。
注意:对于编译器,即使启用了 -O0 来关闭优化,还是会进行一些必要的优化方式,如:减少不必要的复制。
ClassTest ct3 = ct1
该构造函数与第二个例子原理相同,区别是,第二个例子需要先构造一个临时对象,而该例子中已经存在一个对象,所以进行拷贝构造。
值得说明的是:如果该类中不存在复制初始化,编译器会默认生成一个复制初始化,该初始化方式为对应字段简单的拷贝。
ClassTest ct4(ct1)
根据参数确定对应的构造函数 : ClassTest(const ClassTest& ct);
ClassTest ct5 = ClassTest(“ab”)
出乎意料的是通过输出结果来看,该方式并没有临时对象的拷贝,该方式是直接将ct5的地址作为实参去调用构造函数。
ClassTest ct6 = f1()
在调用f1的时候,也传进了ct6对象的地址,在f1内部对c进行初始化后,直接通过c对象地址和ct6地址调用移动构造函数,对ct6进行了初始化,最后返回的是ct6对象地址。
f2(ct1)
一般来说,编译器对于一个非引用类型的变量,不是直接传入ct1对象地址,而是在栈上生成一个临时对象并且用拷贝构造函数进行初始化,最后再传入临时对象的地址调用f2函数。
总结
- 复制初始化:将一个已有的对象(包括临时对象)拷贝到正在创建的对象(一般用
=
)。 - 直接初始化:通过括号给对象提供一定的参数,根据参数匹配对应的构造函数。
- 编译器的优化总结为尽可能的避免使用临时对象。而是选择在对象初始化时去选择对应的构造函数。
本文是原创文章,采用CC BY-NC-SA 4.0协议,完整转载请注明来自tiny-sky 的个人博客