我一直记得返回对象的函数在调用时会有拷贝构造动作,但是最近实际测试却和记忆有些偏差,经查询是编译的问题
RVO: return value optimization
NRVO: named return value optimization
这两个是编译器的一种函数返回值优化策略
先说结果,VS在debug模式下默认 RVO,release模式下默认 NRVO;而g++在debug和release下都默认NRVO
先看一组代码
class Test { public: explicit Test(int num) : num(num) { cout << "constructor " << num << endl; } Test(const Test &test) { cout << "copy constructor " << test.num << endl; num = test.num; } ~Test() { cout << "destructor " << num << endl; } void print() const { cout << "print " << num << endl; } private: int num{}; }; Test getTest(int num) { Test test(num); return test; } int main() { Test test2 = getTest(12); test2.print(); return 0; }
函数 getTest() 返回一个对象,main() 函数中调用并复制给变量理应有一个拷贝构造的动作,但是实际上返回值为
constructor 12 print 12 destructor 12
打印变量地址也发现,getTest() 函数内的变量 test 和 main() 函数中的变量 test 的地址居然是一样的。是我记错了吗?其实不是,根据C++语法,确实应该有拷贝构造的动作,这里的结果是编译器优化的后的,就是上文所说的 RVO 和 NRVO
g++ 可以使用参数 -fno-elide-constructors 来关闭优化,CMakeList 使用以下代码关闭
add_compile_options(-fno-elide-constructors)
或者
set(CMAKE_CXX_FLAGS “-fno-elide-constructors ${CMAKE_CXX_FLAGS}”)
如果我们关闭编译器的优化,最后输出的结果应该是
constructor 12 //getTest()函数内构造test对象 copy constructor 12 //getTest()的返回值不能是test对象,需要一个临时变量,使用test对象拷贝构造临时对象_test destructor 12 //getTest()返回,test对象被析构 copy constructor 12 //main()函数使用test2变量接收临时对象_test destructor 12 //临时对象_test被析构 print 12 //print()调用 destructor 12 //main()函数内test2对象被析构
不难理解,我的记忆没错,根据C++语法确实应该有拷贝构造,只不过是编译器优化了