函数如果返回对象,对效率狂而言是一个严重的挫折,因为以by-value方式返回对象,背后隐藏的constructor和destructor都将无法消除。
有的人会返回指针,于是导致下列这种拙劣的语法形式:
const Rational* operator*(const Rational* lhs,const Rational* rhs)
{
return new Rational(lhs*rhs);
}
她会引出一个重要的问题,导致内存泄漏的问题。
另一些程序员可能会返回reference,于是到处一个危险的做法:
const Rational& operator*(const Rational* lhs,const Rational* rhs)
{
Rational result(lhs.a*rhs.a,lhs.b*rhs.b);
return result;
}
此函数返回一个reference,指向一个不再存在的对象,更明确的说,它返回一个reference指向局部对象result,但该局部对象在返回时自动被销毁了,导致返回一个reference指向一个不存在的对象。
总的来说:如果函数一定得以by-value方式返回一个对象,你绝对无法消除之。这是一场错误的战争从效率的眼光来看,你不应该在乎函数返回了一个对象,你应该在乎的是那个对象的成本几何。你需要做的事女里找出某种方法来降低返回对象的成本,而不是想尽办法消除对象本身。
我们可以用某种特殊写法来撰写函数,使它在返回对象时,能够让编译器消除临时对象的成本。我们的伎俩是:返回所谓的constructor arguments(构造函数参数)一取代对象。如:
函数一:const Rational operator*(const Rational* lhs,const Rational* rhs)
{
Rational temp(lhs.a*rhs.a,lhs.b*rhs.b);
return temp;
}<pre name="code" class="cpp">函数二:const Rational& operator*(const Rational* lhs,const Rational* rhs)
{
return Rational(<span style="font-family: Arial, Helvetica, sans-serif;">lhs.a*rhs.a,lhs.b*rhs.b);</span>
}
以上两者的区别在于函数一多创建了一个临时对象。而函数二仅仅创建了一个临时对象。函数二具体的做法是以constructor arguments取代局部对象,当作返回值。
如果你这样调用时:
Rational a=10;
Rational b(1,2);
Rational c=a*b;
C++则会允许编译器将临时对象优化使它们不存在。如上面公式所写,比的编译器会消除operator*内的临时对象及被operator*返回的临时对象。它们可以将return表达式所定义的对象构造于c的内存内。如果编译器这样做,你调用operator*时的临时对象总成本为0也就是说没有任何临时对象需要被产生出来,取而代之的是,您只需要付出一个constructor(用以产生c)的代价。你无法做的比这更好了,因为c是一个命名对象,而命名对象是不能被消除的。
结论:此特殊的优化行为有一个专属的名词:return value optimization。利用函数的return点消除一个局部临时对象(并可能利用函数调用端的某个对象取代),“拥有专属名称”说明它拥有很广泛。