前置++和後置++的區別

 這篇文章不是討論(i++)+(i++)+(i++)的計算結果,更不是討論(i++)+(++i)+(i++)。

 

在C++教程中,我們都會學到:i++和++i是兩個表達式,他們都會對i進行自增,但是呢,作爲表達式,i++的值是自增之前的值,++i的值是自增之後的值。

 

本文在此基礎上,進行一些稍微深入的討論。

 

從操作符重載的角度,看i++和++i的區別,是一個比較好的切入點。

 

操作符重載

假設有一個類Age,描述年齡。該類重載了前置++和後置++兩個操作符,以實現對年齡的自增。

Cpp代碼  收藏代碼
  1. class Age  
  2. {  
  3. public:  
  4.   
  5.     Age& operator++() //前置++  
  6.     {  
  7.         ++i;  
  8.         return *this;  
  9.     }  
  10.   
  11.     const Age operator++(int//後置++  
  12.     {  
  13.         Age tmp = *this;  
  14.         ++(*this);  //利用前置++  
  15.         return tmp;  
  16.     }  
  17.   
  18.     Age& operator=(int i) //賦值操作  
  19.     {  
  20.         this->i = i;  
  21.         return *this;  
  22.     }  
  23.   
  24. private:  
  25.     int i;  
  26. };  

 

從上述代碼,我們可以看出前置++和後置++,有3點不同:

  1. 返回類型不同
  2. 形參不同
  3. 代碼不同
  4. 效率不同

返回值類型的區別

前置++的返回類型是Age&,後置++的返回類型const Age。這意味着,前置++返回的是左值,後置++返回的是右值。

左值和右值,決定了前置++和後置++的用法。

Cpp代碼  收藏代碼
  1. int main()  
  2. {  
  3.     Age a;  
  4.   
  5.     (a++)++;  //編譯錯誤  
  6.     ++(a++);  //編譯錯誤  
  7.     a++ = 1;   //編譯錯誤  
  8.     (++a)++;  //OK  
  9.     ++(++a);  //OK  
  10.     ++a = 1;   //OK  
  11. }  

 

a++的類型是const Age,自然不能對它進行前置++、後置++、賦值等操作。

++a的類型是Age&,當然不能對它進行前置++、後置++、賦值等操作。

 

a++的返回類型爲什麼要是const對象呢?

有兩個原因:

  1. 如果不是const對象,a(++)++這樣的表達式就可以通過編譯。但是,其效果卻違反了我們的直覺 。a其實只增加了1,因爲第二次自增作用在一個臨時對象上。
  2. 另外,對於內置類型,(i++)++這樣的表達式是不能通過編譯的。自定義類型的操作符重載,應該與內置類型保持行爲一致 。

a++的返回類型如果改成非const對象,肯定能通過編譯,但是我們最好不要這樣做。

 

++a的返回類型爲什麼是引用呢?

這樣做的原因應該就是:與內置類型的行爲保持一致。前置++返回的總是被自增的對象本身。因此,++(++a)的效果就是a被自增兩次。

 

形參的區別

前置++沒有形參,而後置++有一個int形參,但是該形參也沒有被用到。很奇怪,難道有什麼特殊的用意?

其實也沒有特殊的用意,只是爲了繞過語法的限制 。

 

前置++與後置++的操作符重載函數,函數原型必須不同。否則就違反了“重載函數必須擁有不同的函數原型”的語法規定。

雖然前置++與後置++的返回類型不同,但是返回類型不屬於函數原型。爲了繞過語法限制,只好給後置++增加了一個int形參。

 

原因就是這麼簡單,真的沒其他特殊用意。其實,給前置++增加形參也可以;增加一個double形參而不是int形參,也可以。只是,當時就這麼決定了。

 

代碼實現的區別

前置++的實現比較簡單,自增之後,將*this返回即可。需要注意的是,一定要返回*this。

後置++的實現稍微麻煩一些。因爲要返回自增之前的對象,所以先將對象拷貝一份,再進行自增,最後返回那個拷貝。

 

在Age的代碼中,後置++利用了前置++來實現自增。這樣做是爲了避免“自增的代碼”重複。

在本例中,自增的代碼很簡單,就是一行++i,沒有必要這樣做。但是在其它自增邏輯複雜的例子中,這麼做還是很有必要的。

 

效率的區別

如果不需要返回自增之前的值,那麼前置++和後置++的計算效果都一樣。但是,我們仍然應該優先使用前置++,尤其是對於用戶自定義類型的自增操作。

前置++的效率更高,理由是:後置++會生成臨時對象。

 

從Age的後置++的代碼實現也可以看出這一點。

Cpp代碼  收藏代碼
  1. const Age operator++(int//後置++  
  2. {  
  3.     Age tmp = *this;  
  4.     ++(*this);  //利用前置++  
  5.     return tmp;  
  6. }  

 很明顯,tmp是一個臨時對象,會造成一次構造函數和一次析構函數的額外開銷。雖然,編譯器在某些情況下可以優化掉這些開銷。但是,我們最好不要依賴編譯器的行爲。

 

關於前置++和後置++,基本上就是這些內容。如果還有其他需要注意的地方,歡迎補充。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章