返回值的开销
1 class Foo { };
2
3 Foo rbv();
4
5 void yourCode()
6 {
7 Foo x = rbv(); ← the return-value of rbv() goes into x
8
9 }
上面代码中有多少个临时对象?在大多数的商用编译器中会对其进行优化,对上面的代码而言,编译器会做如下的优化:
1 Foo rbv()
2 {
3
4 return Foo(42, 73); ← suppose Foo has a ctor Foo::Foo(int a, int b)
5 }
编译器直接返回构造函数,实际上编译器是通过指针传递来达到这个目的。当在代码调用rbv(),编译器会产生一个指向本地对象(Foo(42,73)),这里说void*还不如说是Foo*。
1 // Pseudo-code
2 void rbv(void* put_result_here) ← Original C++ code: Foo rbv()
3 {
4
5 }
6
7 // Pseudo-code
8 void yourCode()
9 {
10 struct Foo x;
11 rbv(&x); ← Original C++ code: Foo x = rbv()
12
13 }
这里编译器也把这个技巧用在构造函数中,如下面的例子所示;
1 // Pseudo-code
2 void Foo_ctor(Foo* this, int a, int b) ← Original C++ code: Foo::Foo(int a, int b)
3 {
4
5 }
事实上编译做了如下的工作:通过put_result_here来替代构造函数的this指针。
1 // Pseudo-code
2 void rbv(void* put_result_here) ← Original C++ code: Foo rbv()
3 {
4
5 Foo_ctor((Foo*)put_result_here, 42, 73); ← Original C++ code: return Foo(42,73);
6 return;
7 }
回到第一段代码,我们通过&x到rbv(),rbv()传递x到构造函数(像这个this指针),也就直接构造了x。这种返回值优化会使效率更高,即使不打开编译器的优化开关,因为它产生的代码比较少。
一些编译器还支持回返回值优化来返回本地(局部)变量,还提供所有的函数返回本地(局部)量,但不是所有的编译器都支持,例如下面的代码,g++支持而VC不支持。
1 // Actual C++ code for rbv()
2 Foo rbv()
3 {
4
5 Foo ans = Foo(42, 73);
6
7 do_something_with(ans);
8
9 return ans;
10 }
这里编译器可能会产生一个本地(局部)变量ans,在return时拷贝构造ans通过指向put_result_here(的指针)
并析构, 如果所有的函数都返回一个变量,那么编译器也支持构造ans通过put_result_here,如下所示
注意这里讨论的仅针对返回值,例如:如果Foo x=rbv()改成Foo x;x=rbv(),编译器会使用赋值操作符,除非提供默认的构造函数,其次这个赋值操作符和要和拷贝构造功能一样,在return时依然会有临时量产生,在使用赋值操作符拷贝它,再析构,这里的返回值优化也只是它(临时量)的一部分,但是如果改成Foo x,x = rbv(),将程序无法防止编译器消除这个临时量。