2.1 RVO
那么如果我们将main中的两句简化为一句:
xx=return_test();
结果又如何呢 运行之后结果如下:
我们发现不再有赋值运算符被调用,也就是“复制2”不在发生,但是拷贝构造函数依然调用,说明“复制1”还是发生了。这是什么原因呢
首先,我们必须清楚C++创建对象的方式,不要以为创建对象都要调用无参构造函数,当有“=”时是需要调用拷贝构造函数的(这句话也不准确,祥见系列三)。那么具体是如何发生的呢 这里我们介绍一下C++编译器采用的一个优化Return Value Optimization(RVO)。
RVO:
RVO其实就是编译器直接将返回对象通过拷贝构造函数构建在目的对象位置上,而不是临时对象(这种情况不再有临时对象);
具体到编译器是这样执行的:
1. 首先为return_test加上一个额外参数(隐含参数);类型是Class Object的一个引用(同C程序中的临时变量的目的地址,不过此处不再是临时变量,而是目的对象的地址)。这个参数用来放置被“拷贝构建”而返回的值。
2. return前安插了一个拷贝构造函数的调用操作,一边将返回的对象的内容(return_test中的局部变量x)当做上述新增参数的初值。
也就是说return_test将会被改变为:
void return_test(X& result)
{
X x;
result.X::X(x); //编译器产生的操作
return;
}
而对 xx=return_test();的调用也会被修改为
X xx;//(注:这里不再调用构造函数,只分配空间)
return_test(xx);
2.2 扩展
如果return_test().memfun()又该怎么被修改调用呢(其中memfun是X的成员函数)
答案是:X temp;(注:此处仍不会调用构造函数,只是开辟空间,内容由随后的拷贝构造函数构造)
(return_test(temp),temp).memfun();(注意逗号表达式)
总结:无论采用什么方式,C++返回对象时总是伴随至少一次复制,所以应尽量避免返回对象。