20.返回值优化

前言

 
一个返回对象的函数难以保持高效,因为构造与析构所带来的开销无法避免。


问题实例

 
以与Rational类相关的operator*函数为例:

1
2
3
4
5
6
7
8
class Rational {
public:
Rational(int numerator = 0, int denominator = 1);
...
int numerator() const;
int denominator() const;
};
const Rational operator*(const Rational& lhs,const Rational& rhs)

在Effective C++中已经多次以它为例,显然,任何以不返回对象为目的做出的修改都是徒劳的。


优化

 
必须返回对象并不意味着不可以优化,从效率的角度而言,我们更应该把注意力集中到临时对象所带来的开销上,而不是去消除对象。

返回构造函数表达式

1
2
3
4
const Rational operator*(const Rational& lhs,const Rational& rhs){
return Rational(lhs.numerator() * rhs.numerator(),
lhs.denominator() * rhs.denominator());
}

显然,我们通过构造函数构造了一个临时的Rational对象,函数把它拷贝给了返回值。
这种不存在局部对象的方法依旧存在开销,比如说,函数体内临时对象的构造与释放依旧需要开销。但是,C++允许编译器优化不出现的临时对象(temporary objects out of existence)因此,在如下的语句中:

1
2
3
Rational a = 10;
Rational b(1, 2);
Rational c = a * b;

其实并没有出现临时对象,你仅仅付出了一个构造函数的成本,因为编译器直接在为c分配的内存中构造return表达式定义的对象。

如果还要更进一步的话,可以把operator*声明为inline,减少函数的调用开销,这已经达到了优化的最高水平:

1
2
3
4
inline const Rational operator*(const Rational& lhs,const Rational& rhs){
return Rational(lhs.numerator() * rhs.numerator(),
lhs.denominator() * rhs.denominator());
}