http://blog.chinaunix.net/uid-25872711-id-3018672.html
Efficient C ++ 第四章 RVO
返回值优化 Return Value Optimization,简称RVO
RVO是由编译器实现的,其优化原理如以下示例代码:
class Complex { // Complex addition operator friend Complex operator+(const Complex&, const Complex&); public: // Default constructor. // Value defaults to 0 unless otherwise specified. Complex (double r = 0.0, double i = 0.0) : real (r), imag (i) {} // Copy constructor Complex (const Complex& c) : real (c.real), imag (c.imag) {} // Assignment operator Complex& operator= (const Complex& c); ~Complex() {} private: double real; double imag; };
看到有下面操作符的重载定义:
Complex operator+ (const Complex& a, const Complex& b) { Complex retVal; retVal.real = a.real + b.real; retVal.imag = a.imag + b.imag; return retVal; }
假设c1、c2、c3都是Complex类型,我们执行如下操作:
c3 = c1 + c2;
编译器是如何调用operator+的呢?大致伪码如下:
//创建一个临时__result对象,并做为参数传入如下函数原型 void Complex_Add (const Complex& __result, const Complex& c1, const Complex& c2) { ... } //那么c3 = c1 + c2;实际执行了 struct Complex __tempResult; // 临时对象,不构造 Complex_Add(__tempResult,c1,c2); // 传入参数 c3 = __tempResult; // 把临时对象赋值给c3
不做优化的情况下Complex_Add内部为:
void Complex_Add(const Complex& __tempResult, const Complex& c1, const Complex& c2) { struct Complex retVal; retVal.Complex::Complex(); //调用构造函数 retVal.real = a.real + b.real; retVal.imag = a.imag + b.imag; __tempResult.Complex::Complex(retVal);//拷贝构造__tempResult retVal.Complex::~Complex(); //析构retVal return; }
可以看的出来,这里存在retVal的构造和析构的浪费。编译器可以通过消除局部对象retVal来做优化,这就是返回值优化:
void Complex_Add (const Complex& __tempResult, const Complex& c1, const Complex& c2) { __tempResult.Complex::Complex(); // Construct __tempResult __tempResult.real = a.real + b.real; __tempResult.imag = a.imag + b.imag; return; }
经过时间测试,发现使用RVO前后的执行时间相差50%左右。
因为RVO是由编译器选择性使用,如存在多个返回分支的情况一般编译器不会使用RVO。如下:
Complex operator+ (const Complex& a, const Complex& b) // operator+ version 1. { Complex retVal; retVal.real = a.real + b.real; retVal.imag = a.imag + b.imag; return retVal; } Complex operator+ (const Complex& a, const Complex& b) // operator+ version 2. { double r = a.real + b.real; double i = a.imag + b.imag; return Complex (r,i); }
发现编译器对version 1没有使用RVO,version 2有使用。有些编译器对命名变量作为返回值的函数不做RVO,而不进行命名直接通过构造函数返回是可以的。
我们应该了解编译器RVO开启条件,并且尽量使用可以进行优化的编码风格,如version 2。