本文总结以下我对返回值优化的学习,主要参考了下面这个链接的学习内容。
[C++中的返回值优化(RVO)]
命名返回值优化(NRVO)
二话不说,先上代码:
#include <iostream>
class A{
public:
A(){ std::cout << "Default constructor is called." << std::endl; }
A( const A& rhs ){ std::cout << "Copy constructor is called." << std::endl; }
A& operator=( const A& rhs ){
std::cout << "operator=() is called." << std::endl;
return *this;
}
~A(){ std::cout << "Destructor is called." << std::endl; }
};
A foo(){
std::cout << "******foo is entered!********" << std::endl;
A a;
std::cout << "******foo is leaved!*********" << std::endl;
return a;
}
int main( void ){
A b;
b = foo();
return 0;
}
首先先讨论未优化的情形:
编译时加入-fno-elide-constructors 选项,禁止NRVO。
g++ -g -fno-elide-constructors -o main main.cpp
-fno-elide-constructors
The C++ standard allows an implementation to omit creating a
temporary which is only used to initialize another object of the
same type. Specifying this option disables that optimization, and
forces G++ to call the copy constructor in all cases.
/*未优化结果
Default constructor is called. // b的构造函数
******foo is entered!********
Default constructor is called. // a的构造函数
******foo is leaved!*********
Copy constructor is called. // 通过a调用拷贝构造,生成临时对象
Destructor is called. // a析构
operator=() is called.// 临时对象完成对b的赋值
Destructor is called. // 临时对象析构
Destructor is called. // b析构
/*
从未优化的结果来看,如果返回一个对象的情形,在未优化时会用这个命名对象调用拷贝构造生成一个临时对象,然后命名对象析构,临时对象再调用赋值函数进行赋值,临时对象再析构。
返回值不优化时,会生成临时对象。
下面讨论返回值优化的情形
从上面的分析我们应该可以猜到,返回值如果优化,会避免生成临时对象,可以用命名对象直接对左值进行赋值。
/*
Default constructor is called. // b的构造函数
******foo is entered!********
Default constructor is called. // a的构造函数
******foo is leaved!*********
operator=() is called. // 命名对象a直接对b进行赋值
Destructor is called. // a析构
Destructor is called. // b析构
*/
果然,避免了临时对象的生成。
再看一段代码,这段代码主要是为了测试直接进行构造的情形:
#include <iostream>
class A{
public:
A(){ std::cout << "Default constructor is called." << std::endl; }
A( const A& rhs ){ std::cout << "Copy constructor is called." << std::endl; }
A& operator=( const A& rhs ){
std::cout << "operator=() is called." << std::endl;
return *this;
}
~A(){ std::cout << "Destructor is called." << std::endl; }
};
A foo(){
std::cout << "******foo is entered!********" << std::endl;
A a;
std::cout << "******foo is leaved!*********" << std::endl;
return a;
}
int main( void ){
A b = foo();
return 0;
}
直接给结果:
/*
******foo is entered!********
Default constructor is called. // 构造对象a
******foo is leaved!*********
Copy constructor is called. // 通过对象a构造临时对象
Destructor is called. // 对象a析构
Copy constructor is called. // 临时对象拷贝构造b
Destructor is called. // 临时对象析构
Destructor is called. // b析构
*/
下面我们开启优化选项,可以想得到是,应该避免了临时对象的生成。这样就会直接用a去拷贝构造b,然后a析构。
/*
******foo is entered!********
Default constructor is called. // 构造a
******foo is leaved!*********
Destructor is called. // 析构b
*/
发现没有出现我们希望看到的情形,并不是说我们的考虑不到位,而是它优化的太厉害了,连拷贝构造都没有,对于这种优化情形的解释咱是不清楚,先记住这种情形。
未命名返回值优化
先上代码
#include <iostream>
class A{
public:
A(){ std::cout << "Default constructor is called." << std::endl; }
A( const A& rhs ){ std::cout << "Copy constructor is called." << std::endl; }
A& operator=( const A& rhs ){
std::cout << "operator=() is called." << std::endl;
return *this;
}
~A(){ std::cout << "Destructor is called." << std::endl; }
};
A foo(){
std::cout << "******foo is entered!********" << std::endl;
std::cout << "******foo is leaved!*********" << std::endl;
return A();
}
int main( void ){
A b;
b = foo();
return 0;
}
/*
Default constructor is called. //生成b对象
******foo is entered!********
******foo is leaved!*********
Default constructor is called. //生成返回对象
Copy constructor is called. // 用返回对象调用拷贝构造生成临时对象
Destructor is called.// 返回对象析构
operator=() is called. // 临时对象对b进行赋值
Destructor is called. // 临时对象析构
Destructor is called. // 对象b析构
*/
上面的结果是没有开编译优化时的情形,这点和之前是一样的,虽然没有显示的生成第一部分讨论的对象a,但是还是生成了一个返回对象。然后用这个返回对象又去生成了临时对象。
下面我们开启优化选项,这样就可以避免临时对象的生成。
/*
Default constructor is called. // 生成对象b
******foo is entered!********
******foo is leaved!*********
Default constructor is called.// 生成返回对象
operator=() is called. // 用返回对象完成赋值
Destructor is called. // 返回对象析构
Destructor is called. // 对象b析构
*/