1 返回值优化现象 RVO
如下代码,在 MakeObj() 中创建了一个局部对象 obj,并将 obj 返回。 Test() 函数调用了 MakeObj(),并将 MakeObj() 的返回值赋值给了 obj。
按我们的预期,MakeObj() 是值返回,在 main() 调用 Test() 的过程中,应该发生了两次构造和两次析构。
第一次构造:默认构造,在 MakeObj() 声明局部变量的时候。
第二次构造:拷贝构造,在 MakeObj() 返回的时候,将返回值赋值给 Test() 中的局部变量 obj 时
第一次析构:在函数 Test() 中,MakeObj() 返回值赋值给 Test() 中的 obj 完成时, MakeObj() 中的局部变量析构
第二次析构:在函数 Test() 返回时,Test() 函数中的 obj 析构
#include <iostream>class Obj {
public:Obj(int i) {i_ = i;std::cout << "Obj(), i: " << i_ << ", this: " << this << std::endl;}Obj(const Obj &obj) {i_ = obj.i_;std::cout << "copy constructor, i: " << i_ << ", this: " << this << std::endl;}~Obj() {std::cout << "~Obj(), i: " << i_ << ", this: " << this << std::endl;}int i_;};Obj MakeObj() {int a = 10;Obj obj(1);int b = 10;printf("1, &obj = %p, &a = %p, &b = %p, sizeof(obj) = %d\n", &obj, &a, &b, sizeof(obj));return obj;
}void Test() {printf("before make obj\n");int a = 100;Obj obj = MakeObj();int b = 100;printf("2, &obj = %p, &a = %p, &b = %p\n", &obj, &a, &b);printf("after make obj\n");
}int main() {Test();printf("before main return\n");return 0;
}
编译之后运行,现象与我们预期的并不是一致的。如下图所示,只发生了一次构造和一次析构。
并且在 MakeObj() 和 Test() 中均把 obj 的地址打印出来了,两个对象的地址是一样的。
2 禁用返回值优化:-fno-elide-constructors
g++ 编译的时候,默认开启了返回值优化,如果想要禁用返回值优化,那么在编译时需要带上标志 -fno-elide-constructors。
如下图所示,禁用返回值优化之后,运行过程与我们预期的是一致的。
3 局部变量在函数返回的时候没有销毁吗
从上边的实验可以看出,默认情况下,编译器开启了返回值优化。但是从常识来看,函数返回的时候,局部变量(栈空间)要释放,对象会销毁。
那么开启返回值优化的时候,函数返时,难道局部对象没有销毁 ?
开启返回值优化的时候,并没有违反栈销毁的规律。
从上边的打印结果可以看出来,在函数 MakeObj() 以及 Test() 函数中,局部变量 obj 前后分别定义了局部变量 a 和 b,把 a 和 b 的地址也打印出来了。
局部变量都是保存在栈上,一个函数中的多个局部变量的地址应该是相邻的才对。但是看打印结果,obj 的地址和 MakeObj() 中的 a 和 b 的地址并不是相邻的,而是和 Test() 中的 a 和 b 是相邻的。这就是编译器起的作用,编译器判断变量的生命周期,然后决定将局部变量放在哪个位置。