协助完成返回值的优化

一个返回对象的函数很难有较高的效率, 因为传值返回会导致调用对象内的构造和析构函数,这种调用是不能避免的。问题很简单:一个函数要么为了保证正确的行为而返回对象要么就不这么做。如果它返回了对象,就没有办法摆脱被返回的对象。就说到这。 


考虑rational(有理数)类的成员函数operator*: 

class Rational { 
public: 
  Rational(int numerator = 0, int denominator = 1); 
  ... 
  int numerator() const; 
  int denominator() const; 
}; 
  
// 有关为什么返回值是const的解释 
const Rational operator*(const Rational& lhs, 
                         const Rational& rhs); 
甚至不用看operator*的代码,我们就知道它肯定要返回一个对象,因为它返回的是两
个任意数字的计算结果。这些结果是任意的数字。operator*如何能避免建立新对象来容纳它们的计算结果呢?这是不可能的,所以它必须得建立新对象并返回它。不过C++程序员仍然花费大量的精力寻找传说中的方法,能够去除传值返回的对象
 
 时人们会返回指针,从而导致这种滑稽的句法: 
// 一种不合理的避免返回对象的方法 
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;//WQ加注 返回时,其指向的对象已经不存在了 

这个函数返回的引用,其指向的对象已经不存在了。它返回的是一个指向局部对象

result的引用, 当operator* 退出时result被自动释放。 返回指向已被释放的对象的引用,这样的引用绝对不能使用。 


相信我:一些函数(operator*也在其中)必须要返回对象。这就是它们的运行方法。

不要与其对抗,你不会赢的。 你消除传值返回的对象的努力不会获得胜利。 这是一场错误的战争。 从效率的观点来看,你不应该关心函数返回的对象,你仅仅应该关心对象的开销。你所应该关心的是把你的努力引导到寻找减少返回对象的开销上来,而不是去消除对象本身(我们现在认识到这种寻求是无用的) 。如果没有与这些对象相关的开销,谁还会关心有多少对象被建立呢? 以某种方法返回对象, 能让编译器消除临时对象的开销, 这样编写函数通常是很普遍的。


这种技巧是返回constructor argument而不是直接返回对象,你可以这样做: 
// 一种高效和正确的方法,用来实现 
// 返回对象的函数 
const Rational operator*(const Rational& lhs, 
                         const Rational& rhs) 

  return Rational(lhs.numerator() * rhs.numerator(), 
                  lhs.denominator() * rhs.denominator()); 

仔细观察被返回的表达式。它看上去好象正在调用Rational的构造函数,实际上确是
这样。你通过这个表达式建立一个临时的Rational对象, 
Rational(lhs.numerator() * rhs.numerator(),          lhs.denominator() * rhs.denominator()); 
并且这是一个临时对象,函数把它拷贝给函数的返回值。 
返回constructor argument而不出现局部对象,这种方法还会给你带来很多开销,因

你仍旧必须为在函数内临时对象的构造和释放而付出代价, 你仍旧必须为函数返回对象的构造和释放而付出代价。但是你已经获得了好处。C++规则允许编译器优化不出现的临时对象(temporary objects out of existence) 。


因此如果你在如下的环境里调用operator*:  

Rational a = 10; 
Rational b(1, 2);  
Rational c = a * b;                          // 在这里调用operator* 
 编译器就会被允许消除在operator*内的临时变量和operator*返回的临时变量。它们
能在为目标c分配的内存里构造return表达式定义的对象。如果你的编译器这样去做,

用operator*的临时对象的开销就是零:没有建立临时对象。你的代价就是调用一个构造函数――建立c时调用的构造函数。而且你不能比这做得更好了,因为c是命名对象,命名对象不能被消除 。


不过你还可以通过把函数声明为inline来消除operator*

的调用开销 : 
// the most efficient way to write a function returning 
// an object 
inline const Rational operator*(const Rational& lhs, 
                                const Rational& rhs) 

  return Rational(lhs.numerator() * rhs.numerator(), 
                  lhs.denominator() * rhs.denominator()); 

 

“好,不错” ,你嘀咕地说, “优化,谁关心编译器能做什么?我想知道它们确实做了什

么,Does any of this nonsense work with real compilers?” It does。这种特殊的优
化――通过使用函数的return 位置(或者在函数被调用位置用一个对象来替代)来消除局部临时对象――是众所周知的和被普遍实现的。它甚至还有一个名字:返回值优化(return value optimization)
(在《深度探索C++物件模型》中有更多更详细的讲述,它叫之为named return value optimization。但注意,这种优化对普通的赋值运算无效,编译器不能够用拷贝构造函数取代赋值运算动作,最终结论是:在确保语意正确的前题下没有更好的优化可能了) 。实际上这种优化有自己的名字本身就可以解释为什么它被广泛地使用。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值