=====================================================================
如果喜欢,请关注:JellyThink | 思想的果冻
更多原创精彩博文,尽在www.jellythink.com
还可以关注新浪微博:http://weibo.com/u/1887014677
=====================================================================
Named Return Value(实际上就是返回值优化)
当一个函数返回一个对象的实例,一个临时对象被创建并通过拷贝构造函数传回其值。但是在C++标准中,允许省略拷贝构造函数的对象(前提是所有的路径返回相同的对象),这个也是每个C++编译器义不容辞的责任。
有如下的代码:
#include using namespace std; class TestA { public : TestA(){cout<< "Constructor" <<endl;} ~TestA(){cout<< "Destructor" <<endl;} TestA( const TestA& test) { cout<< "Copy Constructor" <<endl; } }; TestA GetTest() { TestA test; return test; } int main() { TestA testA; testA = GetTest(); return 0 ; }
函数GetTest获得一个类的对象,在编译器内部,对于函数GetTest的实际处理会是怎么样的?我先前的博客中有写到,调用和不调用Copy Constructor的时机,而现在的GetTest函数现在就是需要调用Copy Constructor的一种情况。输出如下:
Constructor Constructor Copy Constructor Destructor Destructor Destructor 请按任意键继续. . .
在实际运行上述代码的时候,会输出Copy Constructor,是的,调用了Copy Constructor。这样正好符合我之前的博文中所说的那样。函数GetTest对于编译器来说,会被扩展的,如下:
void GetTest(TestA &result) { TestA test; test.TestA::TestA(); //constructor // Do something with test object // ... result.TestA::TestA(test); return ; }
就像上述函数GetTest函数被编译器展开后的样子,所有的路径都返回相同的Named Value,因此编译器可以做自己的优化。直接以result取代Named Return Valued. 是的,编译器是要进行优化的。默认的情况下,visual studio 2010是关闭NRV的,我们可以在项目属性->配置属性->C/C++->优化中是选择禁用的,所有没有进行优化,你可以打开这个开关(/o2),然后编译上述程序,Copy Constructor是不会调用的。这就是编译器的优化,程序输出如下:
Constructor Constructor Destructor Destructor 请按任意键继续. . .
可以看到,实际少的一次Constructor和一次Destructor正好说明在GetTest函数中的那个生成的result少了,没有了对result的生成和析构。现在是有Copy Constructor而被优化掉了,没有被调用,如果没有Copy Consturctor呢?我把上面的Copy Consturctor注释掉。运行结果如下:
Constructor Constructor Destructor Destructor Destructor 请按任意键继续. . .
使用MinGW进行编译结果如下(即使有了Copy Consturctor也是一样):
Constructor Constructor Destructor Destructor 请按任意键继续. . .
你会感到很惊讶,怎么会不一样呢?
在《深度探索C++对象模型》一书中有说,Copy Constructor的出现激活了C++编译器中的NRV优化,NRV优化并不通过其它的独立的优化工具完成。可见,这样得到的结果和书中所述是有出路的。事实上,即使是没有定义拷贝构造函数和析构函数,而是由编译器产生(在需要时),NRV依然会对程序的效率产生影响。而Visual Studio 2010中少的那次Constructor调用就是默认拷贝构造函数的调用。
在进行大量对象的复制时,NRV能优化程序,提高效率;但是对于NRV带来的副作用,你可以这么想,就比如在Visual Studio 2010中,刚刚那次隐晦的调用Copy Constructor你知道它的调用吗?也就是说编译器觉的合适,就会生成一个默认的构造函数,但是什么是合适的,我现在不知道,我觉得我也应该不知道,就是因为这种不知道,才给我们日后的编程中埋下了隐患。比如:你的类中有一个static变量记录这个对象被拷贝的次数,由于这种隐患,就很可能给程序生成的结果与预期的不一致;同时,因为NRV,构造函数也不会得到调用,而有的时候,我们期望有些在Copy
Constructor中的语句能得到调用,但是如果有了NRV,那就不可能了。
所以,最后,需不需要NRV,视你的具体情况而定。
2013/2/4 于东软-大连
=====================================================================
如果喜欢,请关注:JellyThink | 思想的果冻
更多原创精彩博文,尽在www.jellythink.com
还可以关注新浪微博:http://weibo.com/u/1887014677
=====================================================================