编译器对临时变量的优化--简单理解
2010-7-6
烛秋
2010-8-20整理
一、问题来源
之前对NRV优化做了一点总结,还有所欠缺,只分析了 A b = f();的情况,没有去分析A b; b = f();的情况。又想起之前看过的书,对产生临时对象的讲解都是假设了编译器没有进行优化,然后说要提高C++程序性能,必须尽量的少使用返回对象,因为那样才不会产生临时对象。多使用引用/指针可以减少临时对象的产生,提高程序性能,这个观点没有错。但事实上很多编译器多做了优化,使得临时对象的产生减少了很多,打算以最简单的例子对编译器的优化做简单的理解。
代码:
/* */ ///////////////////////////////////////////////// /* *命令行编译: *g++ -o 1.exe 1.cpp */ ///////////////////////////////////////////////// ///////////////////////////////////////////////// #include <iostream> #include <cstring> using namespace std; class A { public: A() { cout<<"construct"<<endl; strcpy(name,"zhangsan"); } ~A() { cout<<"destruct"<<endl; } A(const A& temp) { cout<<"copy"<<endl; strcpy(name,temp.name); } A& operator= (const A& temp) { cout << "operator=" << endl; strcpy(name, temp.name); return *this; } private: char name[16]; }; //////////////////////////// A f() { cout<<"------------function f----------"<<endl; A b; return b; } //////////////////////////// int main() { A b; b = f();//A b(f()); return 0; } /* 测试输出结果: VS2005release模式//优化了 construct ------------function f---------- construct operator= destruct destruct */ /* vs2005Debug模式//未优化,跟书上的一致 construct ------------function f---------- construct copy destruct operator= destruct destruct */
二、分析
对于采用了NRV优化的编译器来说(例如:VS的release模式、G++),没有临时对象产生。但是对于debug模式下,会产生临时对象。
Debug模式下做法是:局部对象-->临时对象-->外部对象。
说明:第一个箭头调用拷贝构造函数产生了临时对象,第二个箭头调用operator=操作。
Release模式下做法是:局部对象-->外部对象。
说明:调用operator=操作
从图1和图3可以看到,调用operator=操作都是在main()函数中进行,这就意味着operator=的右值必须是存在的对象。在debug模式下,临时对象的地址是在main()函数作用域中,这没有问题。在release模式下,“临时对象"和局部对象合并了。原因如下:局部对象是在函数f()中创建的,但是它的地址空间是main()函数的,从图3和图4可以看到创建局部对象时,使用的地址空间是在main()中申请的“临时对象”空间。这点跟上一篇文章分析的NRV优化类似,都是把临时对象的地址传递进函数f(),然后局部对象就在这个地址上创建,这样就减少了拷贝构造函数的调用。
三、OD调试截图
图 1 debug模式main()函数
图 2 debug模式f()函数
图 3 release模式main()函数
图 4 release模式f()函数