C++简明分析临时对象是什么
作者:清风自在 流水潺潺
对性能来说,许多的问题都需要和出现频率及本身执行一次的开销挂钩,有些问题虽然看似比较开销较大,但是很少会执行到,那也不会对程序有大的影响;同样一个很小开销的函数执行很频繁,同样会对程序的执行效率有很大影响。本章中作者主要根据临时对象来阐述这样一个观点
一、初探临时对象
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++ 编译器会尽力避开临时对象
- 实际工程开发中需要人为的避开临时对象
到此这篇关于C++简明分析临时对象是什么的文章就介绍到这了,更多相关C++临时对象内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!