自增運算子
++
、自減運算子
--
都可以被過載,但是它們有前置、後置之分。
以
++
為例,假設 obj 是一個 CDemo 類的物件,
++obj
和
obj++
本應該是不一樣的,前者的返回值應該是 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++)中,不進行後置形式的過載,則後置形式的表示式就會編譯出錯。