C++中的RVO与NVR优化

语义上,函数调用结束,返回值会通过拷贝构造一个临时匿名对象传出来(因为函数体中的都是局部变量,return后的对象调用完成都超过作用域,不存在了)。

先上代码:

#include <iostream>
using namespace std;

class MyClass
{

private:
	int m_i;

public:
	MyClass(){ m_i = 0; cout << "this is default constructor!" << endl; }  //缺省构造函数
	MyClass(const MyClass& that);   //拷贝构造函数
	MyClass& operator=(const MyClass& that);   //赋值构造函数
};

MyClass::MyClass(const MyClass& that)

{
	cout << "this is copy constructor!" << endl;
	this->m_i = that.m_i;
}

MyClass& MyClass::operator=(const MyClass& that)

{
	cout << "this is assignment constructor!" << endl;

	if (this != &that)
	{
		return *this;
	}
	else
	{
		this->m_i = that.m_i;
		return *this;
	}
}

MyClass gfunc()
{
	MyClass obj;
	return obj;
}

MyClass func()
{
	return MyClass();
}

int main()
{
	MyClass myObj;
	cout << "________" << endl;

	myObj = gfunc();
	cout << "________" << endl;

	MyClass myObj2 = gfunc();
	cout << "________" << endl;

	MyClass myObj3 = func();   //RVO优化
        cout << "________" << endl;

        myObj3 = func();           //RVO优化 
        cin.get();
	return 0;
}

 

 

 

 

 

在VS2013下,最终的输出结果如下:

myObj = gfunc()这句,一共有3次构造函数的调用。包括gfunc()函数体内部的局部对象obj的构造,return返回值一个匿名的临时对象的构造。gfunc()函数体执行完成,return之后的obj对象已经消亡,为了把返回值传出来,必须借助于这个临时对象。

 

借助于C++编译器的RVO技术(return value optimization),返回值优化,return 后是调用构造函数:若是用来给对象赋值,则会省掉一次拷贝构造函数的调用(用来传出返回值的匿名临时对象),代码中的 myObj3 = func();若是用来初始化对象,那么还可以省掉一次赋值构造函数的调用,代码中的  MyClass myObj3 = func()。(注意这里要区别C++中的初始化和赋值,初始化是分配空间的同时赋值,一个语句完成;而赋值是先前已经有了空间)。

 

如果return 之后,是一个具名的对象,编译器可以做 NRV 优化(Named Return Value Optimization),具名返回值优化。此时如果返回值用来初始化对象,可以省掉一次赋值构造函数的调用。 代码中的  MyClass myObj2 = gfunc() ,相当于直接用把return 后的临时对象拷贝构造到myObj2中。

上边两条是编译器默认就提供的,比如在VS下。

如果编译器的优化能力更强,还存在更强的NVR优化技术,能省掉拷贝构造函数的调用。

在gfunc()函数中,内部通过默认构造函数局部变量obj,最后又return这个局部变量obj,对于MyClass myObj2 = gfunc() 语句,完全可以直接将myObj2 代替obj,可以在前边的基础上省掉了拷贝构造函数的调用。myObj = gfunc() 则可以直接将return 后的结果赋值给myobj ,也省掉了拷贝构造函数的调用。

上边的代码在GCC -O优化编译下,输出为:

可以看到如果return 后是一个具名变量,也就是是个左值;NRV优化可以省去拷贝构造一个临时匿名变量。GCC下比较好开启,VS中暂时没找到启用方法。

 

由于NRV优化的存在,可以return之后直接返回一个具名的对象。万一不支持NRV技术,将会拷贝构造一个匿名的临时对象,而且如果存在移动拷贝构造函数,默认的总是移动拷贝构造函数的效率更好,所以当存在移动拷贝构造函数时,将是移动拷贝构造一个临时匿名对象,虽然此时return 后的是一个具名的左值 result,实际相当于 return move(result).

看这里的栗子

上边这个栗子中,函数的返回值类型不能是指向临时变量的引用(无论左值引用还是右值引用)和指针的原则是不能变的;不过可以将函数的返回值(本身可以是个临时变量),赋值给右值引用或者const 左值引用。注意前后的差别。因为函数调用结束,返回值变不存在,实际调用结束返回值是return 后的值的一个拷贝,return 后的那个在调用结束一定是不存在的。

右值引用和const左值引用,会延长临时变量的生存周期,使得引用变量存在时,临时变量也是存在的。

 

 

 
 
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值