美文网首页
C++之返回值优化

C++之返回值优化

作者: Jesse的宽客作坊 | 来源:发表于2016-11-15 18:43 被阅读0次

返回值优化(Return value optimization,缩写为RVO)是C++的一项编译优化技术。它最大的好处是在于: 可以省略函数返回过程中复制构造函数的多余调用,解决 “C++ 中长久以来为人们所诟病的临时对象的效率问题”。

我们先谈正常情况下函数调用的过程。

class RVO
{
public:
  RVO(){printf("I am in constructor\n");}
  RVO (const RVO& c_RVO) {printf ("I am in copy constructor\n");}
  ~RVO(){printf ("I am in destructor\n");}
  int mem_var;      
};
RVO MyMethod (int i)
{
  RVO rvo;
  rvo.mem_var = i;
  return (rvo);
}
int main()
{
  RVO rvo;
  rvo=MyMethod(5);
}

其中非常关键的地方在于对MyMethod函数的编译处理。

RVO MyMethod (int i)
{
  RVO rvo;
  rvo.mem_var = i;
  return (rvo);
}

如果没有返回值优化这项技术,那么实际上的代码应该是编译器处理后的代码应该是这样的:

RVO MyMethod (RVO &_hiddenArg, int i) 
{
  RVO rvo; 
  rvo.RVO::RVO(); 
  rvo.member = i ;  
  _hiddenArg.RVO::RVO(rvo); 
  return;
  rvo.RVO::~RVO(); 
}
  1. 首先,编译器会偷偷地引入一个参数RVO & _hiddernArg,该参数用来引用函数要返回的临时对象,换句话说,该临时对象在进入函数栈之前就已经建立,该对象已经拥有的所属的内存地址和对应的类型;但对应内存上的二进制电位状态尚未改变,即尚未初始化。
    以上涉及到一点变量的概念。变量本质上是一个映射单位,每个映射单位里都有三个元素:变量名、类型、内存地址。变量名是一个标识符。当要对某块内存写入数据时,程序员使用相应的变量名进行内存的标识,而地址这个元素就记录了这个内存的地址位置。而相应类型则告诉编译器应该如何解释此地址所指向的内存,因为本质上,内存上有的仅仅只是两种不同电位的组合而已。因而变量所对应的地址所标识的内存的内容叫做此变量的值。有兴趣的同学可以详细阅读一下这篇文章C++从零开始(三)
  2. RVO rvo; 这里我们创建一个变量——RVO类的对象rvo;计算机将圈定一块内存地址为该变量使用,并声明类型,告诉编译器以后要怎么解释这块内存。
  3. rvo.RVO::RVO(); 但是以上操作尚未改变该内存上的 二进制的电位状态;改变电位状态的工作由rvo对象的构造函数完成。
  4. _hiddenArg.RVO::RVO(rvo); 用rvo对象来调用 临时对象 的拷贝构造函数 来对临时对象进行构造。
  5. rvo.RVO::~RVO(); 函数返回结束; 析构函数内部定义的所有对象。

总结一下一般的函数调用过程中的变量生成传递:

  1. 在函数的栈中创建一个名为rvo的对象
  2. 关键字 return 后的rvo 表示的用变量rvo来构造需要返回的临时对象
  3. 函数返回结束,析构掉在函数内建立的所有对象
  4. 继续表达式rvo=MyMethod(5);里的操作
  5. 语句结束,析构临时对象

这里,在函数栈里创建的对象rvo在函数MyMethod返回时就被析构,其唯一的操作仅仅是调用函数的返回对象、即所谓的临时对象的复制构造函数,然后就被析构了。特别的,如果对象rvo是一个带有大量数据的变量,那么这一次变量的创建与销毁的开销就不容小觑。

但是,如果开启了返回值优化,那么当编译器识别出了 return后的返回对象rvo和函数的返回对象的类型一致,就会对代码进行优化 。编译器转而会将二者的直接关联在一起,意思就是,对rvo的操作就相当于直接对 临时对象的操作,因而编译器处理后的代码应该是这样的:

RVO MyMethod(RVO &_hiddenArg, int i)
{
  _hiddenArg.RVO::RVO();
  _hiddenArg.member = i;
  Return
}

可以发现,优化后的函数依然可以处理相同的工作,但是省略掉了一次复制构造。
因而,在编译时启用以及不启用 RVO 将产生一下两种不同的行为:

  1. 若不启用 RVO,预期输出将是:

I am in constructor
I am in constructor
I am in copy constructor
I am in destructor
I am in destructor
I am in destructor

  1. 若启用 RVO,预期输出将是:

I am in constructor
I am in constructor
I am in destructor
I am in destructor

进一步的,我们也可以预见 使用如下的Method函数也将会有相同的输出结果

RVO MyMethod_01 (int i)
{
  return RVO(i);
}
RVO MyMethod_02 (int i)
{
  return MyMythod_01(int i);
}

这里再稍稍拓展另一个很重要的优化技术,称为复制省略或者译作“省略不必要的复制”(copy elision)。继续以上述代码为例,当用RVO类型的临时对象去初始化同类型的对象时,复制初始化通常优化为直接初始化;但在语义上仍然需要复制构造函数是可访问的。
比如RVO rvo=MyMethod();返回的将是

I am in constructor
I am in destructor

这块详细可见维基百科。复制省略

参考:

  1. https://zh.wikipedia.org/wiki/%E8%BF%94%E5%9B%9E%E5%80%BC%E4%BC%98%E5%8C%96
  2. https://msdn.microsoft.com/zh-cn/library/ms364057(v=vs.80).aspx

相关文章

  • C++之返回值优化

    返回值优化(Return value optimization,缩写为RVO)是C++的一项编译优化技术。它最大的...

  • C++之RVO返回值优化

    什么是RVO优化 RVO的全称是Return Value Optimization。RVO是一种编译器优化技术,可...

  • C++返回值优化(RVO)

    RVO是C++在处理一个函数返回类对象并将返回值赋给另一个对象时,为了减少拷贝构造次数以及析构次数而采用的一种编译...

  • JMeter(7) 优化判断返回类型和返回值

    之前判断接口类型和返回值用beanshell,可见JMeter(5)-JMeter之BeanShell使用 优化点...

  • C++ copy elision and return valu

    复制省略和返回值优化 复制省略和返回值优化是编译器可能存在的优化机制。今天在测试右值没有std::move的时候是...

  • C/C++ 返回值、参数列表、缺省等区别

    一、返回值 C中:如果函数未指定返回值类型,则默认为int C++中:如果一个函数没有返回值,返回值类型必须指定为...

  • C++ RVO

    刷知乎的时候看到这个问题,C++函数返回值拷贝问题?,觉得有趣,因此写了这篇博客。 函数如何返回值 函数返回值的传...

  • SpringMVC之 响应结果和数据视图

    1.响应之返回值是String 2.响应之返回值是void 3.响应之返回值是ModeAndView 4.响应之返...

  • C++plus6th 第7章函数(上)

    第七章 函数(模块) 1. 关于函数返回值 在C++中,函数分为有返回值和无返回值两种。函数可以返回除数组之外的任...

  • 2018-10-23 step

    C++ 11 几个特性的整理 std::future - std::promise 解决的问题 返回值的异步获取:...

网友评论

      本文标题:C++之返回值优化

      本文链接:https://www.haomeiwen.com/subject/kfklpttx.html