一个返回对象的函数很难有较高的效率,因为值传递返回会导致对象的构造和析构的成本,这种调用不可避免。
考虑rational(有理数)类的成员函数operator*:
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);
不用看operator*的代码,就知道它肯定返回一个对象,因为它返回的是两个任意数字的计算结果。这些结果是任意数字,operator*不能避免建立新对象并返回。
有时候人们返回的指针,从而导致滑稽的结果:
const Rational* operator*(const Rational& lhs,const Rational& rhs);
Rational a= 10;
Rational b(1,2);
Rational c = *(a*b); //看起来很奇怪
它会引发一个问题,调用者应该删除函数对象返回的指针,并且通常导致资源泄露。
其他一些开发人员会返回引用,看上去合理,但是却是危险的:
const Rational& operator*(const Rational& lhs,const Rational& rhs);
Rational a= 10;
Rational b(1,2);
Rational c = a*b; //看起来合理
但是函数的实现却是危险的:
const Rational& operator*(const Rational& lhs,const Rational& rhs)
{
Rational result(lhs.numerator()*rhs.numerator()),
lhs.denominator()*rhs.denominator());
return result;
}
函数返回的是引用,当函数返回的时候,其指向的对象却不存在了。
对于一些函数(operator*之类的)必须返回其对象。
我们可以采用某种特殊写法来撰写函数,使他在返回对象时,能够让编译器消除临时对象的成本。
技术是返回所谓的constructor arguments以取代对象。
你可以这么做:
const Rational operator*(const Rational& lhs,const Rational& rhs)
{
return Rational (lhs.numerator()*rhs.numerator()),
lhs.denominator()*rhs.denominator());
}
此时返回的是临时对象,C++允许编译器将临时对象优化,使它们不存在,于是你如此这样调用
operator*:
Rational a = 10;
Rational b(1,2);
Rational c = a*b;
你的编译器得以消除“operator*内的临时对象”以及“被operator*返回的临时对象”。它们可以将return表达式所定义的对象构造于c的内存内。这样你调用operator*的临时对象总成本为0,也就是没有任何临时对象被产出来。取而代之,你只需付出一个constructor(用于产生c)。你可以将此函数声明为inline,以消除调用operator*所花费的额外开销。
真正的编译器都有做这些优化工作,此特殊的优化行为——利用函数的return点消除一个局部临时对象(并可能用函数端调用的某个对象取代),专属名称叫return value optimization(返回值最优化)。