• 条款23: 必须返回一个对象时不要试图返回一个引用


    class rational {
    public:
      rational(int numerator = 0, int denominator = 1);
    
      ...
    
    private:
      int n, d;              // 分子和分母
    
    friend
      const rational                      // 参见条款21:为什么
        operator*(const rational& lhs,    // 返回值是const,若不是const,那么可以对两数的乘积结果进行修改,这不符合默认的乘法规则,如(a*b)=c,会编译错误
                  const rational& rhs)     
    };
    
    inline const rational operator*(const rational& lhs,
                                    const rational& rhs)
    {
      return rational(lhs.n * rhs.n, lhs.d * rhs.d);
    }

    请记住,引用只是一个名字,一个其它某个已经存在的对象的名字。无论何时看到一个引用的声明,就要立即问自己:它的另一个名字是什么呢?因为它必然还有另外一个什么名字。拿operator*来说,如果函数要返回一个引用,那它返回的必须是其它某个已经存在的rational对象的引用,这个对象包含了两个对象相乘的结果。

    但,期望在调用operator*之前有这样一个对象存在是没道理的。

    如果operator* 一定要返回这样一个数的引用,就必须自己创建这个数的对象。一个函数只能有两种方法创建一个新对象:在堆栈里或在堆上。在堆栈里创建对象时伴随着一个局部变量的定义,采用这种方法,就要这样写operator*:

    // 写此函数的第一个错误方法
    inline const rational& operator*(const rational& lhs,
                                     const rational& rhs)
    {
      rational result(lhs.n * rhs.n, lhs.d * rhs.d);
      return result;
    }

    这个方法应该被否决,因为我们的目标是避免构造函数被调用,但result必须要象其它对象一样被构造。另外,这个函数还有另外一个更严重的问题,它返回的是一个局部对象的引用,函数返回后局部对象会被析构。

    基于堆的对象是通过使用new产生的,所以应该这样写operator*:

    // 写此函数的第二个错误方法
    inline const rational& operator*(const rational& lhs,
                                     const rational& rhs)
    {
      rational *result =
        new rational(lhs.n * rhs.n, lhs.d * rhs.d);
      return *result;
    }

    首先,你还是得负担构造函数调用的开销,因为new分配的内存是通过调用一个适当的构造函数来初始化的(见条款5和m8)。另外,还有一个问题:谁将负责用delete来删除掉new生成的对象呢?

    实际上,这绝对是一个内存泄漏。即使可以说服operator*的调用者去取函数返回值地址,然后用delete去删除它(操作很麻烦——条款31展示了这样的代码会是什么样的),但一些复杂的表达式会产生没有名字的临时值,程序员是不可能得到的。例如:

    rational w, x, y, z;
    
    w = x * y * z;

    两个对operator*的调用都产生了没有名字的临时值,程序员无法看到,因而无法删除。

  • 相关阅读:
    Metasploit:一颗没有发现的珍珠
    每个人都用自己的方式去爱自己在乎的人
    设计模式学习使用go实现原型模式 Zhan
    设计模式学习使用go实现代理模式 Zhan
    设计模式学习使用go实现桥接模式 Zhan
    设计模式学习使用go实现建造者模式 Zhan
    多internet出口浮动静态+IP SLA track
    使用 IP SLA 跟踪配置基于策略的路由 (PBR) 自动重定向流量
    使用IP SLA配置静态路由跟踪(基本)
    (转)PBR路由策略配置
  • 原文地址:https://www.cnblogs.com/ljygoodgoodstudydaydayup/p/3916353.html
Copyright © 2020-2023  润新知