不同於普通的雙目運算符“+/-”,自增自減運算符“++/--”在重載的時候需要額外留意區分是前置式還是後置式。現行的處理方法是,(作為成員函數的)前置“++”不接受任何參數,而後置“++”接受一個int類型的參數,盡管沒什麼實際用途,但是卻為編譯器確定重載對象提供了幫助。
除了區分的時候需要注意,他們的行為也是有所區別的。援引More Effective C++中的話,前置++是increment and fetch,而後置++則是fetch and increment。所以我們在重載這些運算符時也要符合這些要求。
還有一點,則是需要特殊小心地處理返回值問題。前置++通常情況應該返回對象的引用,而後置式的++應該返回一個const修飾的對象實體。
前置++返回引用的理由很簡單,比如對前置++擁有如下行為:
++(++a)
首先需要明確的是,運算符實際上也是一個函數調用,也是具有返回值的。那麼第二次++是對第一次++的返回值進行操作。如果我們不返回一個引用而返回一個新建的臨時對象,那麼第二次++得到的便是對這個臨時對象再進行自增操作的結果,從而導致這條語句對實際的對象a,只能夠進行一次自增操作。可以查看其反匯編代碼,能夠證實這一點。而後置的++返回一個對象實體,則也很簡單,因為你在後置++中,需要臨時保存舊的值,而這個舊的值必須是後置++的函數體內部的臨時變量。返回一個臨時變量的指針或者引用是危險的、不允許的。至於為什麼需要const修飾,則是要保證a++++這類表達式在編譯時期非法而不是到運行時期出現不期望的結果。為什麼不能出現a++++,一是內置的數據類型(比如int)不允許我們這樣做;二是就算自定義的類能夠實現這種行為,它也是不符合你的一般常識性想法的:我們通常認為兩個++並在一起會導致a連續遞增兩次,而正如上面分析前置++連用,返回對象而不是引用,則讓第二次的操作對象為一個臨時對象而不是其本身,與我們預期不一致。為了避免這種誤會,必須要用const修飾符來顯式禁止。函數返回const對象能夠確保不作用在非const的成員函數上,而operator++確實是非const成員函數。
另外,建議在重載後置++時使用++(*this),而前置++使用*this += 1。因為支持自增運算的對象必然支持加法運算,這樣能夠顯著提高代碼的簡潔性和可讀性。編譯器的內聯函數優化也並不會增加多少執行速度上的額外開銷(相比於在++內部直接寫函數體)。
這樣我們就得出了通用的、重載++操作符的范例代碼了:
1 //prefix ++ : increment and fetch 2 myclass & myclass::operator++() 3 { 4 *this += 1; 5 return *this; 6 } 7 //postfix ++ : fetch and increment 8 const myclass myclass::operator++(int) 9 { 10 myclass tmp = *this; 11 ++(*this); 12 return tmp; 13 }
關於效率上,通常有“前置式自增”效率較佳,因為前置運算符不需要產生臨時對象和對臨時對象進行構造函數和析構函數的調用。如果這個對象比較大,內存復制的時間也是較大的。因此,如果不是特殊需要,一般盡可能使用前置++於類類型。這也是似乎在運用STL迭代器時,盡可能在循環使用前置++的原因。
參考:More Effective C++,Meyer Scott,侯捷譯,電子工業出版社