返回值优化(Return Value Optimization,简称RVO)是一种编译器优化机制:当函数需要返回一个对象的时候,如果自己创建一个临时对象用于返回,那么这个临时对象会消耗一个构造函数(Constructor)的调用、一个复制构造函数的调用(Copy Constructor)以及一个析构函数(Destructor)的调用的代价。

经过返回值优化,就可以将成本降低到一个构造函数的代价。这样就省去了一次拷贝构造函数的调用和依次析构函数的调用。

例子如下:

class MyString {
public:
MyString() {
_data = NULL;
_len = 0;
printf("Constructor is called!\n");
}
MyString(const char* p) {
_len = strlen (p);
_init_data(p);
cout << "Constructor is called! this->_data: " << (long)_data << endl;
}
MyString(const MyString& str) {
_len = str._len;
_init_data(str._data);
cout << "Copy Constructor is called! src: " << (long)str._data << " dst: " << (long)_data << endl;
} ~MyString() {
if (_data)
{
cout << "DeConstructor is called! this->_data: " << (long)_data << endl;
free(_data);
}
else
{
std::cout << "DeConstructor is called!" << std::endl;
}
}
MyString& operator=(const MyString& str) {
if (this != &str) {
_len = str._len;
_init_data(str._data);
}
cout << "Copy Assignment is called! src: " << (long)str._data << " dst" << (long)_data << endl;
return *this;
} operator const char *() const {
return _data;
} void display() const
{
if (_data)
{
cout << "str is " << _data << "(" << (long)_data << ")" << endl;
}
else
{
cout << "nothing" << endl;
}
}
private:
char *_data;
size_t _len;
void _init_data(const char *s) {
_data = new char[_len+1];
memcpy(_data, s, _len);
_data[_len] = '\0';
}
}; MyString foo1()
{
return MyString("123");
} MyString foo2()
{
MyString str1("456");
return str1;
} int main()
{
foo1();
cout << "--------------------\n"; foo2();
cout << "--------------------\n"; MyString str1 = foo1();
cout << "--------------------\n"; MyString str2 = foo2();
cout << "--------------------\n";
return 0;
}

函数foo1直接返回一个临时对象,而foo2返回一个局部变量。在没有RVO的情况下,不管是调用foo1还是foo2,实际上都是先调用构造函数,然后调用复制构造函数构造作为返回值的临时对象。而对于str1和str2的构造,还会再次调用一次复制构造函数。上述代码,使用的编译命令为:g++ -fno-elide-constructors -o rvo rvo.cpp

-fno-elide-constructors选项可以取消编译器的 copy-elision 优化策略。得到的结果如下:

Constructor is called! this->_data: 8949776

Copy Constructor is called! src: 8949776 dst: 8949808

DeConstructor is called! this->_data: 8949776

DeConstructor is called! this->_data: 8949808

--------------------

Constructor is called! this->_data: 8949808

Copy Constructor is called! src: 8949808 dst: 8949776

DeConstructor is called! this->_data: 8949808

DeConstructor is called! this->_data: 8949776

--------------------

Constructor is called! this->_data: 8949776

Copy Constructor is called! src: 8949776 dst: 8949808

DeConstructor is called! this->_data: 8949776

Copy Constructor is called! src: 8949808 dst: 8949776

DeConstructor is called! this->_data: 8949808

--------------------

Constructor is called! this->_data: 8949808

Copy Constructor is called! src: 8949808 dst: 8949840

DeConstructor is called! this->_data: 8949808

Copy Constructor is called! src: 8949840 dst: 8949808

DeConstructor is called! this->_data: 8949840

--------------------

DeConstructor is called! this->_data: 8949808

DeConstructor is called! this->_data: 8949776

如果编译时去掉了-fno-elide-constructors选项,则编译器开启RVO,结果如下:

Constructor is called! this->_data: 34054160

DeConstructor is called! this->_data: 34054160

--------------------

Constructor is called! this->_data: 34054160

DeConstructor is called! this->_data: 34054160

--------------------

Constructor is called! this->_data: 34054160

--------------------

Constructor is called! this->_data: 34054192

--------------------

DeConstructor is called! this->_data: 34054192

DeConstructor is called! this->_data: 34054160

可见开启了RVO之后,省略了不必要的复制拷贝,开启RVO之后,函数是直接在接收返回值的地方直接构造对象。

实际上,foo1和foo2分别对应了RVO和NRVO(Named Return Value Optimization)。具名返回值优化(NRVO),是对于按值返回“具名对象”(就是有名字的变量)时的优化手段,其实道理是一样的,但由于返回的值是具名变量,情况会复杂很多。所以,能执行优化的条件更苛刻。比如函数中,在不同的返回路径上返回不同名的对象,就不会执行NRVO。

比如下面的代码:

MyString bar1(int n)
{
if (n > 2)
{
return MyString("abc");
}
else
{
return MyString("ABC");
}
} MyString bar2(int n)
{
MyString str1("abc");
MyString str2("ABC");
if (n > 2)
{
return str1;
}
else
{
return str2;
}
} int main(int argc, char **argv)
{
bar1(1);
cout << "--------------------\n"; bar2(1);
cout << "--------------------\n"; MyString str1 = bar1(1);
cout << "--------------------\n"; MyString str2 = bar2(1);
cout << "--------------------\n";
return 0;
}

函数bar1返回临时对象,bar2返回具名对象,也就是说,如果执行优化的话,bar1执行RVO,而bar2执行NRVO。

首先是加上-fno-elide-constructors选项后的运行结果:

Constructor is called! this->_data: 11149328

Copy Constructor is called! src: 11149328 dst: 11149360

DeConstructor is called! this->_data: 11149328

DeConstructor is called! this->_data: 11149360

--------------------

Constructor is called! this->_data: 11149360

Constructor is called! this->_data: 11149328

Copy Constructor is called! src: 11149328 dst: 11149392

DeConstructor is called! this->_data: 11149328

DeConstructor is called! this->_data: 11149360

DeConstructor is called! this->_data: 11149392

--------------------

Constructor is called! this->_data: 11149392

Copy Constructor is called! src: 11149392 dst: 11149360

DeConstructor is called! this->_data: 11149392

Copy Constructor is called! src: 11149360 dst: 11149392

DeConstructor is called! this->_data: 11149360

--------------------

Constructor is called! this->_data: 11149360

Constructor is called! this->_data: 11149328

Copy Constructor is called! src: 11149328 dst: 11149424

DeConstructor is called! this->_data: 11149328

DeConstructor is called! this->_data: 11149360

Copy Constructor is called! src: 11149424 dst: 11149360

DeConstructor is called! this->_data: 11149424

--------------------

DeConstructor is called! this->_data: 11149360

DeConstructor is called! this->_data: 11149392

加上-fno-elide-constructors选项后,运行结果如下:

Constructor is called! this->_data: 9449488

DeConstructor is called! this->_data: 9449488

--------------------

Constructor is called! this->_data: 9449488

Constructor is called! this->_data: 9449520

Copy Constructor is called! src: 9449520 dst: 9449552

DeConstructor is called! this->_data: 9449520

DeConstructor is called! this->_data: 9449488

DeConstructor is called! this->_data: 9449552

--------------------

Constructor is called! this->_data: 9449552

--------------------

Constructor is called! this->_data: 9449488

Constructor is called! this->_data: 9449520

Copy Constructor is called! src: 9449520 dst: 9449584

DeConstructor is called! this->_data: 9449520

DeConstructor is called! this->_data: 9449488

--------------------

DeConstructor is called! this->_data: 9449584

DeConstructor is called! this->_data: 9449552

对比上面的结果,可见返回临时对象的bar1函数的调用进行了优化。而bar2函数的调用,不管有没有-fno-elide-constructors选项,单独调用bar2返回结果都是一样的,说明没有执行NRVO。对比”MyString str2 = bar2(1);”语句的执行结果,发现加上-fno-elide-constructors选项选项之后,仅仅少了一次复制构造函数的调用,这是因为虽然bar2没有执行NRVO,但是使用bar2返回的临时对象初始化str2时,编译器依然有copy elision的优化策略。

有关copy elision的解释如下:

In C++ computer programming, copy elision refers to a compiler optimization technique that eliminates unnecessary copying of objects.

The standard also describes a few situations where copying can be eliminated even if this would alter the program's behavior, the most common being the return value optimization. Another widely implemented optimization, described in the C++ standard, is when a temporary object of class type is copied to an object of the same type.

(https://en.wikipedia.org/wiki/Copy_elision)

When a nameless temporary, not bound to any references, would be copied or moved (since C++11) into an object of the same type (ignoring top-level cv-qualification), the copy/move (since C++11) is omitted. When that temporary is constructed, it is constructed directly in the storage where it would otherwise be copied or moved (since C++11) to. When the nameless temporary is the argument of a return statement, this variant of copy elision is known as RVO, "return value optimization".

(http://en.cppreference.com/w/cpp/language/copy_elision)

注:以上所有代码的编译环境是:操作系统CentOS Linux release 7.3.1611;GCC版本:gcc version 4.8.5 20150623 (Red Hat 4.8.5-11) (GCC)

参考:

http://blog.csdn.net/gatieme/article/details/22650353

http://www.cnblogs.com/liyiwen/archive/2009/12/02/1615711.html

C++返回值优化的更多相关文章

  1. 返回值优化(RVO)

    C++的函数中,如果返回值是一个对象,那么理论上它不可避免的会调用对象的构造函数和析构函数,从而导致一定的效率损耗.如下函数所示: A test() { A a; return a; } 在test函 ...

  2. 【M20】协助完成“返回值优化(RVO)”

    1.方法返回对象,会导致临时对象的产生,这降低了效率,const Rational operator* (const Rational& lhs,Rational& rhs).有没有什 ...

  3. [转] C++中临时对象及返回值优化

    http://www.cnblogs.com/xkfz007/articles/2506022.html 什么是临时对象? C++真正的临时对象是不可见的匿名对象,不会出现在你的源码中,但是程序在运行 ...

  4. [More Effective C++]条款22有关返回值优化的验证结果

    (这里的验证结果是针对返回值优化的,其实和条款22本身所说的,考虑以操作符复合形式(op=)取代其独身形式(op),关系不大.书生注) 在[More Effective C++]条款22的最后,在返回 ...

  5. C++返回值优化RVO

    返回值优化,是一种属于编译器的技术,它通过转换源代码和对象的创建来加快源代码的执行速度.RVO = return value optimization. 测试平台:STM32F103VG + Keil ...

  6. 转:C++中临时对象及返回值优化

    http://www.cnblogs.com/xkfz007/articles/2506022.html 什么是临时对象? C++真正的临时对象是不可见的匿名对象,不会出现在你的源码中,但是程序在运行 ...

  7. 一段小代码秒懂C++右值引用和RVO(返回值优化)的误区

    关于C++右值引用的参考文档里面有明确提到,右值引用可以延长临时变量的周期.如: std::string&& r3 = s1 + s1; // okay: rvalue referen ...

  8. 返回值优化 RVO

    <深度探索C++对象模型>-- 2.3 返回值的初始化 & 在编译器层面做优化

  9. C++标准库之string返回值研究

    先说结论(不一定适用所有环境): 1) GCC默认开启了返回值优化(RVO),除非编译时指定“-fno-elide-constructors”: 2) 现代C++编译器一般都支持返回值优化: 3) s ...

随机推荐

  1. Jquery手机下拉刷新,下拉加载数据

    一.Jquery手机下拉刷新,下拉加载数据.附加有源码 <!DOCTYPE html> <html> <head> <title>手机</titl ...

  2. 【python之路25】正则表达式

    一.正则表达式简介 就其本质而言,正则表达式(或RE)是一种小型的.高度专业化的(在python中),它内嵌在python中,并通过RE模块实现.正则表达式编译成一系列字节码,然后由用C编写的匹配引擎 ...

  3. TZ_08_maven把第三方 jar 包放入本地仓库或私服

    --安装第三方jar包到本地仓库 需求:首先下载jar包并且找到对应的 -DgroupId=? -DartifactId=? -Dversion=? -Dpackaging=jar  -Dfile=j ...

  4. 读书笔记--Head First 设计模式 目录

    1.设计模式入门 2.观察者模式 3.装饰者模式 4.工厂模式 5.单件模式 6.命令模式 7.适配器模式与外观模式 8.模板方法模式 9.迭代器与组合模式 10.状态模式 11.代理模式 12.复合 ...

  5. Linux中管理员用户与普通用户之间的切换

    使用su进行用户切换 管理员用户切换至普通用户:  su [用户名] 使用su命令从高级别用户切换至低级别用户无需输入密码 普通用户切换至管理员用户:  普通用户切换至管理员用户使用 su - 使用l ...

  6. Color the ball HDU - 1556 (线段树)

    思路:线段树,区间更新 #include<iostream> #include<vector> #include<string> #include<cmath ...

  7. Django orm的惰性机制

    Django惰性机制 所谓惰性机制:Publisher.objects.all()或者.filter()等都只是返回了一个QuerySet(查询结果集对象),它并不会马上执行sql,而是当调用Quer ...

  8. PyCharm常用技巧集合

    PyCharm常用技巧集合 一.添加或者修改文件模板 File>settings>Editor>File and Code Templates>Python Script 你可 ...

  9. Vue.之.路由跳转

    Vue.之.路由跳转 在进行项目开发的过程中,需要使用路由进行跳转.如下: // 不带有参数,在页面上跳转到别的页面 1. this.$router.push('/login/init');  // ...

  10. day37 04-Hibernate二级缓存:list和iterate方法比较

    get()和load()方法既可以向一级缓存区放数据,也可以向二级缓存区放数据.这是查询一个的情况.要是查询所有呢?注意, // 查询所有.Query接口的list()方法. // list()方法会 ...