RVO-编译器返回值优化

RVO--Return Value Optimization

概述

返回值优化(Return Value Optimization,简称RVO)是一种编译器优化机制:

当函数需要返回一个对象的时候,如果自己创建一个临时对象用户返回,那么这个临时对象会消耗一个构造函数(Constructor)的调用、一个复制构造函数的调用(Copy Constructor)以及一个析构函数(Destructor)的调用的代价。而如果稍微做一点优化,就可以将成本降低到一个构造函数的代价,这样就省去了一次拷贝构造函数的调用和依次析构函数的调用。

编译使用原则

在使用GNU/g++编译器时可以使用"-fno-elide-constructors"选项来强制g++总是调用copy构造函数,即使在用临时对象初始化另一个同类型对象的时候。

但是在使用VS架构的编译器的时候, 貌似编译器默认直接使用RVO的, 并没有找到什么选项来关闭返回值优化。。。

具体信息请参照http://msdn.microsoft.com/en-us/library/ms364057(VS.80).aspx

例子

我们都知道三种拷贝构造函数调用的时机

①最明显的就是用一个类对象初始化另外一个对象的时候
②函数的参数是类对象,这个是函数按值传参数的时候,包括指针在内都是对原有的值的拷贝.
③函数返回一个类对象,这是一个对象以值传递的方式从函数返回.

但是这些都只是语义上的分析, 现在的编译器的编译策略, 很多情况下会把这些临时对象的创建都隐藏掉...

[cpp] view plain copy print?

  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4.   
  5. class Base  
  6. {  
  7. public :  
  8.     Base(int num)  
  9.     {  
  10.         m_num = num;  
  11.     }  
  12.     Base(const Base &base)  
  13.     {  
  14.         cout <<"Copy constructor..." <<endl;  
  15.     }  
  16.   
  17.     /*~Base( ) 
  18.     { 
  19.         cout <<"Destroy...." <<endl; 
  20.     } */  
  21. private :  
  22.     int m_num;  
  23.   
  24. };  
  25.   
  26.   
  27.   
  28. Base GetBase( )  
  29. {  
  30.     Base base(10);  
  31.     cout <<"返回值是对象类型, 应该调用拷贝构造函数" <<endl;  
  32.     return base;  
  33. }  
  34.   
  35. void GetBase(Base base)  
  36. {  
  37.     cout <<"参数是对象类型, 应该调用拷贝构造函数" <<endl;  
  38. }  
  39.   
  40.   
  41. int main( )  
  42. {  
  43.     Base b1(20);  
  44.     cout <<"使用一个类对象去初始化另一个类对象, 应该调用拷贝构造函数" <<endl;  
  45.     Base b2 = b1;  
  46.     //cout <<"使用一个可以隐式转换为Base类对象的整数值1来初始化, 应该调用拷贝构造函数" <<endl;  
  47.     //Base b3 = 1;  
  48.     b2 = GetBase( );  
  49.     GetBase( );  
  50. }  
#include <iostream>
using namespace std;

class Base
{
public :
	Base(int num)
	{
	    m_num = num;
	}
	Base(const Base &base)
	{
		cout <<"Copy constructor..." <<endl;
	}

	/*~Base( )
	{
		cout <<"Destroy...." <<endl;
	} */
private :
    int m_num;

};

Base GetBase( )
{
	Base base(10);
	cout <<"返回值是对象类型, 应该调用拷贝构造函数" <<endl;
	return base;
}

void GetBase(Base base)
{
	cout <<"参数是对象类型, 应该调用拷贝构造函数" <<endl;
}

int main( )
{
	Base b1(20);
	cout <<"使用一个类对象去初始化另一个类对象, 应该调用拷贝构造函数" <<endl;
	Base b2 = b1;
	//cout <<"使用一个可以隐式转换为Base类对象的整数值1来初始化, 应该调用拷贝构造函数" <<endl;
	//Base b3 = 1;
	b2 = GetBase( );
	GetBase( );
}

上面的代码,用GCC4.8.1编译, 如果常规进行编译, 就会出现这样的结果

但是如果加上-fno-elide-constructors选项, 调用结果就成为我们所理解的方式

很明显, 默认的RVO设置, 将后面两种情况下, 临时对象的创建优化掉了。。。

编译器无法进行RVO的几种情况

RVO是一个很有帮助的编译优化策略, 但是它的优化实现是依赖于编译器的,也就是说编译器只能对指定的符合其规则的代码, 进行RVO优化,对于不符合要求的却无能为力了,

下面我们总结一下有哪些情况会阻止编译器进行这种优化。

① 函数使用exeception, 为了保证异常的正确捕获, 编译器不会进行RVO

② 不对命名的函数对象(Different Named Objects), 这个情况是说,函数有可能有多个分支,最终导致有多个不同位置的return语句。对于此项优化,要求所有return返回的对象的名字都是一致的。如写成下面的情况,就不行:

[cpp] view plain copy print?

  1. Base Test( )   
  2. {  
  3.     if(...)  
  4.     {  
  5.        return b1;  
  6.     }  
  7.     else  
  8.     {  
  9.     return b2;  
  10.     }  
  11. }  
Base Test( )
{
    if(...)
    {
       return b1;
    }
    else
    {
	return b2;
    }
}

因此 优化的关键,就是最好只在一个地方返回。即函数只有一个出口

③ 嵌入的汇编代码中引用了返回对象。

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

时间: 2024-12-03 14:19:57

RVO-编译器返回值优化的相关文章

Visual C++ 2005中的命名返回值优化

多年来,Microsoft Visual C++编译器一直在努力寻求更新的技术与优化方式,以求最大可能地提高程序的性能.此文描述了Visual C++编译器在不同情况下,是怎样消除多余的复制构造函数和析构函数的. 通常来说,当方法返回对象的一个实例时,会创建一个临时对象,并通过复制构造函数复制到目标对象中.在C++标准中,允许省略复制构造函数(哪怕会导致不同的程序行为),但这有一个副作用,就是编译器可能会把两个对象当成一个.Visual C++ 8.0(Visual C++ 2005)充分利用了

C++中按值返回和返回值优化代码

C++和C语言相比,最为人诟病的就是其性能问题,通常一条C语言经编译器解释后,可以固定转换成5-10条汇编语言,但是一条C++语言,就没有这么幸运了,可能会是3条汇编语言,也可能是300条.C++影响性能的原因很多,其中一个就是临时对象的创建和销毁.这里我简述一种减少创建临时对象的方法--返回值优化问题 很多时候,函数需要按值返回,这其中就会不可避免地涉及到临时对象的创建和销毁.假设定义如下的Complex类: class Complex { friend Complex operator +(

C++返回值为对象时复制构造函数不执行怎么破

先说点背景知识,调用复制构造函数的三种情况: 1.当用类一个对象去初始化另一个对象时. 2.如果函数形参是类对象. 3.如果函数返回值是类对象,函数执行完成返回调用时. 在辅导学生上机时,有同学第3点提出异议.有教材上的例题为证: #include <iostream> using namespace std; class Point //Point 类的定义 { public: Point(int xx=0, int yy=0) { x = xx; //构造函数,内联 y = yy; } P

xmlHTTP返回值重编码的优化

xml|编码|优化 置顶的帖子里有一个VBscript的代码用来将xmlHTTP返回值重编码,不过效率极其低下.....俺费了好大的力气,终于做了一个速度勉强让人满意的代码.不过水平所限还是不得不用了vbscript的chr函数用来将acsii转成unicode,不知道哪位大哥能想想办法?ps:算法优化真是个费脑筋的活. -×-×-×-×-×-×-×-×-华丽的更新分割线-×-×-×-×-×-×-×-×- 俺每句话都下了断点测试运行时间,终于把运行时间缩短了大约10倍,现在应该是一种可以实用的算

对象的初始化-用函数的返回值初始化一个类对象,这其中用了几次复制构造函数

问题描述 用函数的返回值初始化一个类对象,这其中用了几次复制构造函数 这是我自己写的一段代码#includeusing namespace std;class Example{int num;public:Example(int i){num=i;cout<<""This is construction with parameter.n"";}Example(){num=0;cout<<""This is construc

C++构造函数、拷贝构造函数、赋值运算符漫谈(二)——函数返回值

 首先我们先看一下C程序的返回值处理情况,我们知道当C函数返回int等小型数据时直接将返回值放入eax寄存器.那当返回大的数据结构又是如何处理呢?看如下一段代码: #include <stdio.h> typedef struct big_thing { char buf[128]; }big_thing; big_thing return_test() { big_thing b; b.buf[0]=0; retutn b; } int main(int argc, char *argv[]

c语言中返回值和返回指针的问题

int testA (void) { int b = 1 ; return b; } char * testB (void) { char str[] = "abc" ; return str; } int main() { printf( " the value of testA is %d \n", testA() ); printf( " the value of testB is %c ", *( testB() ) ) ; } 对于返回

C/C++误区五:检查 new 的返回值

首先澄清一下,这个误区仅对 C++ 成立,这里不过是沿用"C/C++ 误区"这个衔头罢了. 我们都知道,使用 malloc/calloc 等分配内存的函数时,一定要检查其返回值是否为"空指针"(亦即检查分配内存的操作是否成功),这是良好的编程习惯,也是编写可靠程序所必需的.但是,如果你简单地把这一招应用到 new 上,那可就不一定正确了.我经常看到类似这样的代码: int* p = new int[SIZE]; if ( p == 0 ) // 检查 p 是否空指针

C/C++误区三:强制转换 malloc() 的返回值

首先要说的是,使用 malloc 函数,请包含 stdlib.h(C++ 中是 cstdlib) ,而不是 malloc.h .因为 malloc.h 从来没有在 C 或者 C++ 标准中出现过! 因此并非所有编译器都有 malloc.h 这个头文件.但是所有的 C 编译器都应该 有 stdlib.h 这个头文件. 在 C++ 中,强制转换 malloc() 的返回值 是必须的,否则不能通过编译.但是在 C 中,这种强制转换却是多余的,并且 不利于代码维护. 起初,C 没有 void 指针,那时