C++過載++和--(自增和自減運算子)

2020-07-16 10:04:25
自增運算子++、自減運算子--都可以被過載,但是它們有前置、後置之分。

++為例,假設 obj 是一個 CDemo 類的物件,++objobj++本應該是不一樣的,前者的返回值應該是 obj 被修改後的值,而後者的返回值應該是 obj 被修改前的值。如果如下過載++運算子:
CDemo & CDemo::operator ++ ()
{
    //...
    return * this;
}
那麼不論obj++還是++obj,都等價於obj.operator++()無法體現出差別。

為了解決這個問題,C++ 規定,在過載++--時,允許寫一個增加了無用 int 型別形參的版本,編譯器處理++--前置的表示式時,呼叫引數個數正常的過載函數;處理後置表示式時,呼叫多出一個引數的過載函數。來看下面的例子:
#include <iostream>
using namespace std;
class CDemo {
    private:
        int n;
    public:
        CDemo(int i=0):n(i) { }
        CDemo & operator++();      //用於前置形式
        CDemo operator++( int );   //用於後置形式
        operator int ( ) { return n; }
        friend CDemo & operator--(CDemo & );
        friend CDemo operator--(CDemo & ,int);
};
CDemo & CDemo::operator++()
{//前置 ++
    n ++;
    return * this;
}
CDemo CDemo::operator++(int k )
{ //後置 ++
    CDemo tmp(*this); //記錄修改前的物件
    n++;
    return tmp; //返回修改前的物件
}
CDemo & operator--(CDemo & d)
{//前置--
    d.n--;
    return d;    
}
CDemo operator--(CDemo & d,int)
{//後置--
    CDemo tmp(d);
    d.n --;
    return tmp;
}
int main()
{
    CDemo d(5);
    cout << (d++ ) << ",";  //等價於 d.operator++(0);
    cout << d << ",";
    cout << (++d) << ",";   //等價於 d.operator++();
    cout << d << endl;
    cout << (d-- ) << ",";  //等價於 operator-(d,0);
    cout << d << ",";
    cout << (--d) << ",";   //等價於 operator-(d);
    cout << d << endl;
    return 0;
}
程式執行結果:
5,6,7,7
7,6,5,5

本程式將++過載為成員函數,將--過載為全域性函數。其實都過載為成員函數更好,這裡將--過載為全域性函數只是為了說明可以這麼做而已。

呼叫後置形式的過載函數時,對於那個沒用的 int 型別形參,編譯器自動以 0 作為實參。 如第 39 行,d++等價於d.operator++(0)

對比前置++和後置++運算子的過載可以發現,後置++運算子的執行效率比前置的低。因為後置方式的過載函數中要多生成一個區域性物件 tmp(第21行),而物件的生成會引發建構函式呼叫,需要耗費時間。同理,後置--運算子的執行效率也比前置的低。

前置++運算子的返回值型別是 CDemo &,而後置++運算子的返回值型別是 CDemo,這是因為運算子過載最好保持原運算子的用法。C++ 固有的前置++運算子的返回值本來就是運算元的參照,而後置++運算子的返回值則是運算元值修改前的複製品。例如:
int a = 5;
(++a) = 2;
上面兩條語句執行後,a 的值是 2,因為 ++a 的返回值是 a 的參照。而
(a++) = 2;
這條語句是非法的,因為 a++ 的返回值不是參照,不能作為左值。

--運算子的返回值型別的設定和++運算子一樣。

在有的編譯器(如Visual Studio)中,如果沒有後置形式的過載,則後置形式的自增或自減表示式也被當作前置形式處理。而在有的編譯器(如Dev C++)中,不進行後置形式的過載,則後置形式的表示式就會編譯出錯。