Item M6:自增(increment)、自减(decrement)操作符前缀形式与后缀形式的区别

      很久以前(八十年代),没有办法区分++--操作符的前缀与后缀调用。这个问题遭到程序员的报怨,于是 C++语言得到了扩展,允许重载 increment 和 decrement 操作符的两种形式。 

     然而有一个句法上的问题,重载函数间的区别决定于它们的参数类型上的差异,但是不论是 increment 或 decrement 的前缀还是后缀都只有一个参数。为了解决这个语言问题,C++规定后缀形式有一个 int 类型参数,当函数被调用时,编译器传递一个 做为 int 参数的值给该函数: 

class UPInt { // "unlimited precision int" 

public: 

     UPInt& operator++(); // ++ 前缀 

     const UPInt operator++(int); // ++ 后缀 

     UPInt& operator--(); // -- 前缀 

     const UPInt operator--(int); // -- 后缀 

     UPInt& operator+=(int);  // += 操作符,UPInts 

                                             // 与 ints 相运算 

... 

}; 

    UPInt i; 

    ++i; // 调用 i.operator++(); 

    i++; // 调用 i.operator++(0);

    --i; // 调用 i.operator--(); 

    i--; // 调用 i.operator--(0);


      这个规范有一些古怪,不过你会习惯的。而尤其要注意的是:这些操作符前缀与后缀形式返回值类型是不同的。前缀形式返回一个引用,后缀形式返回一个 const 类型。下面我们将讨论++操作符的前缀与后缀形式,这些说明也同样适用于--操作符。 

     从你开始做 程序员那天开始,你就记住 increment 的前缀形式有时叫做“增加然后取回”,后缀形式叫做“取回然后增加”。这两句话非常重要,因为它们是 increment 前缀与后缀的形式上的规范。 

// 前缀形式:增加然后取回值 

UPInt& UPInt::operator++() 

    *this += 1; // 增加 

    return *this;    // 取回值 

// postfix form: fetch and increment 

const UPInt UPInt::operator++(int

    UPInt oldValue = *this;  // 取回值 

    ++(*this); // 增加 

   return oldValue;  // 返回被取回的值 

      后缀操作符函数没有使用它的参数。它的参数只是用来区分前缀与后缀函数调用。如果你没有在函数里使用参数,许多编译器会显示警告信息,很令人讨厌。为了避免这些警告信息,一种经常使用的方法时省略掉你不想使用的参数名称;如上所示。 

     很明显一个后缀 increment 必须返回一个对象(它返回的是增加前的值),但是为什么是 const 对象呢?假设不是 const 对象,下面的代码就是正确的: 

     UPInt i; 

     i++++; // 两次 increment 后缀 

这组代码与下面的代码相同: 

     i.operator++(0).operator++(0); 

     很明显,第一个调用的 operator++函数返回的对象调用了第二个 operator++函数。 

     有两个理由导致我们应该厌恶上述这种做法,第一是与内置类型行为不一致。当设计一个类遇到问题时,一个好的准则是使该类的行为与 int 类型一致。而 int 类型不允许连续进行两次后缀 increment: 

     int i; 

     i++++; // 错误


     第二个原因是使用两次后缀 increment 所产生的结果与调用者期望的不一致。如上所示,第二次调用 operator++改变的值是第一次调用返回对象的值,而不是原始对象的值。

     因此如果: 

     i++++; 

    是合法的,将仅仅增加了一次。这与人的直觉相违背,使人迷惑(对于 int 类型和 UPInt都是一样),所以最好禁止这么做。 

     C++禁止 int 类型这么做,同时你也必须禁止你自己写的类有这样的行为。最容易的方法是让后缀 increment 返回 const 对象。当编译器遇到这样的代码: 

      i++++; // same as 

      i.operator++(0).operator++(0); 

      它发现从第一个 operator++函数返回的 const 对象又调用 operator++函数,然而这个函数是一个 nonconst 成员函数,所以 const 对象不能调用这个函数。如果你原来想过让一个函数返回 const 对象没有任何意义,现在你就知道有时还是有用的,后缀 increment和 decrement 就是例子。(更多的例子参见 Effective C++ 条款 21) 

      如果你很关心效率问题,当你第一次看到后缀 increment 函数时,你可能觉得有些问题。这个函数必须建立一个临时对象以做为它的返回值,(参见条款 M19),上述实现代码建立了一个显示的临时对象(oldValue),这个临时对象必须被构造并在最后被析构。前缀increment 函数没有这样的临时对象。由此得出一个令人惊讶的结论,如果仅为了提高代码效率,UPInt 的调用者应该尽量使用前缀 increment,少用后缀 increment,除非确实需要使用后increment让我们明确一下,当处理用户定义的类型时,尽可能地使用前缀increment,因为它的效率较高。 

       我们再观察一下后缀与前缀 increment 操作符。它们除了返回值不同外,所完成的功能是一样的,即值加一。简而言之,它们被认为功能一样。那么你如何确保后缀 increment和前缀 increment 的行为一致呢?当不同的程序员去维护和升级代码时,有什么能保证它们不会产生差异?除非你遵守上述代码里的原则,这才能得到确保。这个原则是后increment 和 decrement 应该根据它们的前缀形式来实现。你仅仅需要维护前缀版本,因为后缀形式自动与前缀形式的行为一致。 

     正如你所看到的,掌握前缀和后缀 increment decrement 是容易的。一旦了解了他们正确的返回值类型以及后缀操作符应该以前缀操作符为基础来实现的规则,就足够了。



                                                          ------From  Scott Meyers<<More Effective C++>>


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值