程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> More Effective C++:不要重載的操作符

More Effective C++:不要重載的操作符

編輯:C++入門知識

  與C一樣,C++使用布爾表達式簡化求值法(short-circuit evaluation)。這表示一旦確定了布爾表達式的真假值,即使還有部分表達式沒有被測試,布爾表達式也停止運算。例如:
  
   char *p;
  ...
  
  if ((p != 0) && (strlen(p) > 10)) ...
  這裡不用擔心當p為空時strlen無法正確運行,因為假如p不等於0的測試失敗,strlen不會被調用。同樣:
  
   int rangeCheck(int index)
  {
   if ((index < lowerBound) (index > upperBound)) ...
  ...
  }
  假如index小於lowerBound,它不會與upperBound進行比較。
   
  很早以前上述行為特性就被反復灌輸給C和C++的程序員,所以他們都知道該特性。而且他們也依靠於簡短求值法來寫程序。例如在上述第一個代碼中,當p為空指針時確保strlen不會被調用是很重要的,因為C++標准說(正如C標准所說)用空指針調用strlen,結果不確定。
  
  C++答應根據用戶定義的類型,來定制&&和操作符。方法是重載函數operator&& 和operator,你能在全局重載或每個類裡重載。然而假如你想使用這種方法,你必須知道你正在極大地改變游戲規則。因為你以函數調用法替代了簡短計算法。也就是說假如你重載了操作符&&,對於你來說代碼是這樣的:
  
   if (eXPression1 && expression2) ...
  對於編譯器來說,等同於下面代碼之一:
  
   if (expression1.operator&&(expression2)) ...
  // when operator&& is a
  // member function
  if (operator&&(expression1, expression2)) ...
  // when operator&& is a
  // global function
  這似乎沒有什麼不同,但是函數調用法與簡短求值法是絕對不同的。首先當函數被調用時,需要運算其所有參數,所以調用函數functions operator&& 和 operator時,兩個參數都需要計算,換言之,沒有采用簡短計算法。第二是C++語言規范沒有定義函數參數的計算順序,所以沒有辦法知道表達式1與表達式2哪一個先計算。完全與具有從左參數到右參數計算順序的簡短計算法相反。
  
  因此假如你重載&&或,就沒有辦法提供給程序員他們所期望和使用的行為特性,所以不要重載&&和。
  
  同樣的理由也適用於括號操作符,但是在我們深入研究它之前,我還是暫停一下,讓你不要太驚奇,“逗號操作符?哪有逗號操作符?”確實存在。
  
  逗號操作符用於組成表達式,你經常在for循環的更新部分(update part)裡遇見它。例如下面來源於Kernighan's and Ritchie's 經典書籍The C Programming Language 第二版(Prentice-Hall, 1988)的函數:
  
   // reverse string s in place
  
  void reverse(char s[])
  {
   for (int i = 0, j = strlen(s)-1;i < j;++i, --j) // 啊! 逗號操作符!
   {
  int c = s[i];
  s[i] = s[j];
  s[j] = c;
   }
  }
  在for循環的最後一個部分裡,i被增加同時j被減少。在這裡使用逗號很方便,因為在最後一個部分裡只能使用一個表達式,分開表達式來改變i和j的值是不合法的。
  
  對於內建類型&&和,C++有一些規則來定義它們如何運算。與此相同,也有規則來定義逗號操作符的計算方法。一個包含逗號的表達式首先計算逗號左邊的表達式,然後計算逗號右邊的表達式;整個表達式的結果是逗號右邊表達式的值。所以在上述循環的最後部分裡,編譯器首先計算++i,然後是—j,逗號表達式的結果是--j。
  
  也許你想為什麼你需要知道這些內容呢?因為你需要模擬這個行為特性,假如你想大膽地寫自己的逗號操作符函數。不幸的是你無法模擬。
  
  假如你寫一個非成員函數operator,你不能保證左邊的表達式先於右邊的表達式計算,因為函數(operator)調用時兩個表達式做為參數被傳遞出去。但是你不能控制函數參數的計算順序。所以非成員函數的方法絕對不行。
  
  剩下的只有寫成員函數operator的可能性了。即使這裡你也不能依靠於逗號左邊表達式先被計算的行為特性,因為編譯器不一定必須按此方法去計算。因此你不能重載逗號操作符,保證它的行為特性與其被料想的一樣。重載它是完全輕率的行為。
  
  你可能正在想這個重載惡夢究竟有沒有完。究竟假如你能重載逗號操作符,你還有什麼不能重載的呢?正如顯示的,存在一些限制,你不能重載下面的操作符:
  
   . .* :: ?:
  
  new delete sizeof typeid
  
  static_cast dynamic_cast const_cast reinterpret_cast
  你能重載:
  
   operator new operator delete
  
  operator new[] operator delete[]
  
  + - * / % ^ & ~
  
  ! = < > += -= *= /= %=
  
  ^= &= = << >> >>= <<= == !=
  
  <= >= && ++ -- , ->* ->
  
  () []
  當然能重載這些操作符不是去重載的理由。操作符重載的目的是使程序更輕易閱讀,書寫和理解,而不是用你的知識去迷惑其他人。假如你沒有一個好理由重載操作符,就不要重載。在碰到&&, , 和 ,時,找到一個好理由是困難的,因為無論你怎麼努力,也不能讓它們的行為特性與所期望的一樣。
  
 
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved