More Effective C++ 條款6 (轉)

gugu99發表於2008-07-11
More Effective C++ 條款6 (轉)[@more@]

條款6:自增(increment)、自減(decrement)運算子字首形式與字尾形式的區別:namespace prefix = o ns = "urn:schemas--com::office" />

很久以前(八十年代),沒有辦法區分++和--運算子的字首與字尾。這個問題遭到員的報怨,於是C++語言得到了擴充套件,允許過載increment 和 decrement運算子的兩種形式。

 

然而有一個句法上的問題,過載間的區別決定於它們的引數型別上的差異,但是不論是increment或decrement的字首還是字尾都只有一個引數。為了解決這個語言問題,C++規定字尾形式有一個int型別引數,當函式被呼叫時,傳遞一個0做為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型別。下面我們將討論++運算子的字首與字尾形式,這些說明也同樣使用與--運算子。

從你開始做C程式設計師那天開始,你就記住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++++;

 

是合法的,i將僅僅增加了一次。這與人的直覺相違背,使人迷惑(對於int型別和UPInt都是一樣),所以最好禁止這麼做。

 

C++禁止int型別這麼做,同時你也必須禁止你自己寫的類有這樣的行為。最容易的方法是讓字尾increment 返回const物件。當編譯器遇到這樣的程式碼:

 

i++++;  // same as i.operator++(0).operator++(0);

 

它發現從第一個operator++函式返回的const物件又呼叫operator++函式,然而這個函式是一個non-const成員函式,所以const物件不能呼叫這個函式。如果你原來想過讓一個函式返回const物件沒有任何意義,現在你就知道有時還是有用的,字尾increment和decrement就是例子。(更多的例子參見Effective C++ 條款21)

 

如果你很關心問題,當你第一次看到字尾increment函式時, 你可能覺得有些問題。這個函式必須建立一個臨時物件以做為它的返回值,(參見條款19),上述實現程式碼建立了一個顯示的臨時物件(oldValue),這個臨時物件必須被構造並在最後被結構。字首increment函式沒有這樣的臨時物件。由此得出一個令人驚訝的結論,如果僅為了提高程式碼效率,UPInt的呼叫者應該儘量使用字首increment,少用字尾increment,除非確實需要使用字尾increment。讓我們明確一下,當處理定義的型別時,儘可能地使用字首increment,因為它的效率較高。

 

我們再觀察一下字尾與字首increment 運算子。它們除了返回值不同外,所完成的功能是一樣的,即值加一。簡而言之,它們被認為功能一樣。那麼你如何確保字尾increment和字首increment的行為一致呢?當不同的程式設計師去維護和升級程式碼時,有什麼能保證它們不會產生差異?除非你遵守上述程式碼裡的原則,這才能得到確保。這個原則是字尾increment和decrement應該根據它們的字首形式來實現。你僅僅需要維護字首版本,因為字尾形式自動與字首形式的行為一致。

 

正如你所看到的,掌握字首和字尾increment和decrement是容易的。一旦瞭解了他們正確的返回值型別以及字尾運算子應該以字首運算子為基礎來實現的規則,就足夠了。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10748419/viewspace-1007126/,如需轉載,請註明出處,否則將追究法律責任。

相關文章