C++临时对象
清风自在 流水潺潺 人气:0一、初探临时对象
1.问题
下面的程序输出什么?为什么?
下面编写程序进行实验:
#include <stdio.h> class Test { int mi; public: Test(int i) { mi = i; } Test() { Test(0); } void print() { printf("mi = %d\n", mi); } }; int main() { Test t; t.print(); return 0; }
输出结果如下:
程序意图:
- 在 Test() 中以 0 作为参数调用 Test(int i)
- 将成员变量 mi 的初始值设置为 0
运行结果:
- 成员变量 mi 的值为随机值
2.思考
构造函数是一个特殊的函数
- 是否可以直接调用?
- 是否可以在构造函数中调用构造函数?
- 直接调用构造函数的行为是什么?
3.答案
- 直接调用构造函数将产生一个临时对象
- 临时对象的生命周期只有一条语句的时间(过了这条 C++ 语句,临时对象将被析构而不复存在)
- 临时对象的作用域只在一条语句中
- 临时对象是 C++ 中值得警惕的灰色地带
可以将上面代码写成这样,避免临时对象:
#include <stdio.h> class Test { int mi; void init(int i) { mi = i; } public: Test(int i) { init(i); } Test() { init(0); } void print() { printf("mi = %d\n", mi); } }; int main() { Test t; t.print(); return 0; }
输出结果如下:
再来看一个程序,深刻体会一下临时对象:
#include <stdio.h> class Test { int mi; void init(int i) { mi = i; } public: Test(int i) { printf("Test(int i)\n"); init(i); } Test() { printf("Test()\n"); init(0); } void print() { printf("mi = %d\n", mi); } ~Test() { printf("~Test()\n"); } }; int main() { printf("main begin\n"); Test(); Test(10); printf("main end\n"); return 0; }
输出结果如下:
这个程序很好的说明了临时对象的生命周期只有一条语句的时间(过了这条 C++ 语句,临时对象将被析构而不复存在)
二、编译器的行为
现代 C++ 编译器在不影响最终执行结果的前提下,会尽力减少临时对象的产生!!!
下面来看一个例子:
#include <stdio.h> class Test { int mi; public: Test(int i) { printf("Test(int i) : %d\n", i); mi = i; } Test(const Test& t) { printf("Test(const Test& t) : %d\n", t.mi); mi = t.mi; } Test() { printf("Test()\n"); mi = 0; } int print() { printf("mi = %d\n", mi); } ~Test() { printf("~Test()\n"); } }; Test func() { return Test(20); } int main() { //Test t(10); 等价于 Test t = Test(10); Test t = Test(10); // ==> Test t = 10; Test tt = func(); // ==> Test tt = Test(20); ==> Test tt = 20; t.print(); tt.print(); return 0; }
输出结果如下:
注意两点:
- 通过输出结果可以看到【编译器并没有按照生成临时对象,再用临时对象初始化 t 对象(其中涉及调用拷贝构造函数)】的步骤,因为现代的编译器都会尽力避免临时对象的产生。
- Test t = Test(10); 等价于 Test t = 10; 写成Test t = 10; 可以杜绝临时对象的产生。因为临时对象的产生会带来性能上的问题,Test t = Test(10); 相当于调用了两次构造函数, 而 Test t = 10; 少调用一次函数,性能得到提升。
三、小结
- 直接调用构造函数将产生一个临时对象
- 临时对象是性能的瓶颈,也是 bug 的来源之一
- 现代 C++ 编译器会尽力避开临时对象
- 实际工程开发中需要人为的避开临时对象
加载全部内容