一、more effective c++:不要過載的操作符
與c一樣,c++使用布林表示式簡化求值法(short-circuit evaluation)。這表示一旦確定了布林表示式的真假值,即使還有部分表示式沒有被測試,布林表示式也停止運算。例如:
char *p;
...if ((p != 0) && (strlen(p) > 10)) ...
這裡不用擔心當p為空時strlen無法正確執行,因為如果p不等於0的測試失敗,strlen不會被呼叫。同樣:
int rangecheck(int index)
如果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迴圈的最後乙個部分裡,i被增加同時j被減少。在這裡使用逗號很方便,因為在最後乙個部分裡只能使用乙個表示式,分開表示式來改變i和j的值是不合法的。
對於內建型別&&和||,c++有一些規則來定義它們如何運算。與此相同,也有規則來定義逗號操作符的計算方法。乙個包含逗號的表示式首先計算逗號左邊的表示式,然後計算逗號右邊的表示式;整個表示式的結果是逗號右邊表示式的值。所以在上述迴圈的最後部分裡,編譯器首先計算++i,然後是—j,逗號表示式的結果是--j。
也許你想為什麼你需要知道這些內容呢?因為你需要模仿這個行為特性,如果你想大膽地寫自己的逗號操作符函式。不幸的是你無法模仿。
如果你寫乙個非成員函式operator,你不能保證左邊的表示式先於右邊的表示式計算,因為函式(operator)呼叫時兩個表示式做為引數被傳遞出去。但是你不能控制函式引數的計算順序。所以非成員函式的方法絕對不行。
剩下的只有寫成員函式operator的可能性了。即使這裡你也不能依靠於逗號左邊表示式先被計算的行為特性,因為編譯器不一定必須按此方法去計算。因此你不能過載逗號操作符,保證它的行為特性與其被料想的一樣。過載它是完全輕率的行為。
你可能正在想這個過載惡夢究竟有沒有完。畢竟如果你能過載逗號操作符,你還有什麼不能過載的呢?正如顯示的,存在一些限制,你不能過載下面的操作符:
. .* :: ?:
newdelete
sizeof
typeid
static_cast
dynamic_cast
const_cast
reinterpret_cast
你能過載:
operator new operator delete
operator new operator delete
+ - * / % ^ & | ~
! = < > += -= *= /= %=
^= &= |= << >> >>= <<= == !=
<= >= && || ++ -- , ->* ->
()
當然能過載這些操作符不是去過載的理由。操作符過載的目的是使程式更容易閱讀,書寫和理解,而不是用你的知識去迷惑其他人。如果你沒有乙個好理由過載操作符,就不要過載。在遇到&&, ||, 和 ,時,找到乙個好理由是困難的,因為無論你怎麼努力,也不能讓它們的行為特性與所期望的一樣。
二、過載操作符的設計
①類的設計者不能宣告乙個沒有預定義的過載操作符。
②不能為內建資料型別定義其他的操作符。
③預定義的操作符優先順序不能被改變。
④乙個類最終需要提供哪些操作符,是由該類預期的用途來決定的。
三、prefix and postfix
為區分後置操作符與前置操作符的宣告,過載的遞增和遞減後置操作符的宣告有乙個額外的int 型別的引數。這裡不需要給出引數名,因為它沒有被用在操作符定義中。額外的整型引數對於後置操作符的使用者是透明的,編譯器為它提供了預設值因而該引數也可以被忽略。
例如:#include
#include
using
namespace std;
class person
person const operator++()/*prefix ++ */
person const operator++(int a)/*postfix ++ */
int getage()
};
int main()
四、過載的建議
當乙個過載操作符是乙個名字空間的函式時,對於操作符的第乙個和第二個引數,即等於操作符的左和右兩個運算元,都會考慮轉換.
一般應該怎樣決定是把乙個操作符宣告為類成員還是名字空間成員呢?在某些情況下程式設計師沒有選擇的餘地:
1 如果乙個過載操作符是類成員,那麼只有當跟它一起被使用的左運算元是該類的物件時,它才會被呼叫.如果該操作符的左運算元必須是其他的型別,那麼過載操作符必須是名字空間成員.
2 c++要求賦值= 下標 呼叫() 和成員訪問箭頭-> 操作符必須被定義為類成員操作符.任何把這些操作符定義為名字空間成員的定義都會被標記為編譯時刻錯誤.
例如:// 錯誤: 必須是類成員
char& operator( string & ,int ix );
3 除此之外由類設計者選擇把操作符宣告為乙個類成員還是乙個名字空間成員.如果有乙個運算元是類型別,如string 類的情形,那麼對於對稱操作符,比如等於操作符最好定義為名字空間成員.
C 操作符過載
1.作為成員過載 class myclass public myclass operator const myclass d cons friend myclass operator const myclass a1,const myclass a2 關於返回值型別的討論 呼叫者堆疊裡返回乙個物件效...
C 過載操作符
過載操作符 一 過載操作符的定義 1.過載操作符的結構 返回型別 operator 需要過載的操作符 形參列表 注意 形引數目應和運算元數目相同。2.過載操作符的幾條注意事項 1 過載的操作符名不能通過連線其他合法符號來建立任何新的操作符。如 2 過載操作符必須具備至少乙個類型別或列舉型別的運算元。...
c 操作符過載
過載操作符 一 限制 1 不能增加新的操作符 2 有些操作符不能過載,如.物件中的訪問成員 作用域解析操作符 sizeof 三元操作符 3 不能改變操作符的元數,元數是指與操作符相關的引數或運算元個數。比如一元操作符 只能應用於乙個操作符 4 不能改變操作符的優先順序 5 不能重新定義內建型別的操作...