第8章 運算符重載
8.1 運算符函數據與運算符重載
運算符重載是計算機語言固有多態(tài)性的體現,是構成計算機語言的基礎之一。
C++把重載的運算符視為特殊的函數,稱為運算符函數。運算符重載就是函數重載的一種特殊情況。像對待一般重載函數一樣,編譯系統(tǒng)能夠依據使用運算符的不同環(huán)境,即參數(操作數)的數量或類型的差異,區(qū)分同一運算符的不同含義。
“運算符重載”是針對C++中原有運算符進行的,不可能通過重載創(chuàng)造出新的運算符。除了。、。*、->*、::、?:這五個運算符外,其他運算符都可以重載。由于很多符號是一元運算符和二元運算符公用的,為了避免含混,不得為重載的運算符函數設置默認值,調用時也就不得省略實參。
除了new和delete這兩個較為特殊運算符以外,任何運算符如果作為成員函數重載時不得重載為靜態(tài)函數。=、[ ]、()、->以及所有的類型轉換運算符只能作為成員函數重載,而且不能是針對枚舉類型操作數的重載。
運算符函數的函數名是由運算符前加關鍵字operator構成的,在聲明運算符或調用運算符時都可以用這個名稱。
8.2 典范運算符的重載
1.關于分數類fraction
fraction的聲明和定義包含在頭文件fraction.h和程序文件fraction.cpp中。
一個標準的用fraction表示的分數須滿足以下復印件:
、俜帜赣肋h為正,分數和符號用分子表示;
、诜肿臃帜富ベ|,即總表示為最簡分數。
fraction通過兩個私有數據成員num和den分別保存分子和分母,并在必要時調用standardize函數進行標準化處理,以使num和den的值滿足標準分數的條件。gcd是求兩個整數的最大公約數的函數,standardize在化簡分數時要調用它。
2.重載取負運算符“-”
因為fraction用分子的符號代表整個分數的符號,因此所謂“取負”只需對分子num取負就可以了。由于取負運算符“-”是一元運算符,當作為成員函數重載時參數表中沒有參數,那個唯一的操作數以this指針的形式隱藏在參數表中。為此,只需要在fraction.h的類聲明中增加:
fraction poerator -()const { return fraction(-num,den);}
就可以了。由于在類聲明中直接給出了完整定義,因此是一個inline函數。
“-”是一個典型的一元運算符,除++、--外的其他一元運算符的重載都可以參考這里描述的方法。
3.重載加法運算符“+”
“ +”是一個二元運算符,因此作為成員函數重載時參數表中只有一個參數,對應于第二操作數,而第一操作數就是對象本身,僅以this指針的形式隱藏在參靈敏表中。
“+”是一個典型的二元運算符,除賦值類運算符外的其他二元運算符的重載都可以參考這里描述的方法。
4.重載增1運算符“+ +”
+ +既可以是前綴運算符(前增1),又可以是后綴運算符(后增1)。為了區(qū)分這兩種情況,重載這兩個運算符時必須在格式上有所區(qū)別:重載后綴+ +時必須多一個虛擬參數:int,因此從形式上看像是一個二元運算符重載。
5.重載類型轉換符“l(fā)ong”
類型轉換符必須作為成員函數重載。在重載類型轉換符時,由于運算符本身已經表示出返回值類型,因此不需要返回值類型的聲明。一個分數可以看成是由一個整數部分和一個純分數部分組成的,為了取得一個分數的整數部分,可為fraction重載類型轉換符long.為此可在fraction.h的類聲明中增加:
opertator long()const { return num/den;}
6.重載賦值運算符“=”
賦值運算符只能作為成員函數重載。
常見的真正需要重載賦值運算符的情況是:類中包含指向動態(tài)空間的指針
賦值運算符=的重載應注意以下幾點:
、俜祷刂德暶鳛橐茫瘮刁w中總是用語句return *this;返回;
、谌绻麉当宦暶鳛橹赶蛲悓ο蟮囊没蛑羔,應判別所指向對象的是否與被賦值對象為同一對象,如果是,立即返回,不做任何賦值處理;
、廴绻毁x值對象占用了動態(tài)空間或其他資源,應首先釋放這些資源,以便接收新的資源;
、苋绻麉当宦暶鳛橹羔樆蛞茫ǔ由蟘onst修飾;
、萑绻麉当宦暶鳛橹羔,應判別是否為空,以便做出特殊處理;
、抟粋類如果需要重載運算符=,通常也就需要定義自己特有的拷貝構造函數,反之亦然。
7.重載復合賦值運算符“+=”
重載復合賦值類運算符,如+=、-=等,也應遵循上述重載賦值運算符的注意事項。
與賦值運算符不同的是,復合賦值類運算符既可作為成員函數重載也可作為非成員函數重載。在后一種情況下,兩個操作數都必須出現在參數表中;為了保持運算符原有的特性,第一參數應當聲明為引用(否則就無法改變它的值),返回值也應當像重載“=”那樣聲明為引用,并在最后將獲得新值的第一參數返回。
8.重載關系操作符“>”
重載的關系操作符函數應返回邏輯值。對于 fraction的兩個對象,可以通過比較通分后的兩個分子來確定它們的大小。為此,可在fraction.h的類聲明中增加如下的成員函數聲明:
bool operator>(fraction f){ return num*f.den>f.num*den;}
其他關系運算符可以參照重載。
9重載下標訪問運算符“[ ]”
運算符[ ]只能作為成員函數重載。
10重載C+ +流運算符“”和“”
C+ +流的輸入運算符和輸出運算符只能作為非類成員函數重載。在一個類中,如有必要,可將或聲明為友元函數。
8.3 運算符重載應注意的幾個問題
1.重載的運算符應保持其原有的基本語義
重載的運算符應該體現為原運算符的功能在新的數據類型上的延伸,它的使用應當使程序中算法的表達顯得更流暢、自然,使閱讀程序的人在不借助于其他說明資料的情況下就能夠正確理解。不要讓重載的運算符去勉強承擔那些更適于一般函數承擔的功能。
2.生載的運算符應盡可能保持基原有的特性
運算符的操作數個數、優(yōu)先級和結合性是三個最基本的特性,而且是重載時自然得以保持的特性,因此無須采取專門的措施。需要注意的是下面這些特性。
①是否要求第一操作數為有左值操作數。
、谑欠裥薷牡谝徊僮鲾。
、鄄僮鞯慕Y果是否為有左值數據。
、軕WC第二操作數不被改變。
3.運算符的重載應當配套
某些運算符之間關系密切,存在著某種邏輯上的聯系,因此若需要重載其中的某一個,往往就意味著同組的其他運算符也需要重載。
4.使用引用參數還是非引用參數?
非引用參數的優(yōu)點是:以傳值方式傳遞參數,形參變量只是實參的副本,對形參變量的修改不會影響實參;在相關對象存在只需一個實參的構造函數的情況下,可以充分利用表達式處理過程中的自動轉換機制,使表達式顯得更自然。但當對象很大或需要深層復制時,非引用參數占用的計算機資源較多,影響參數傳遞的效率。
引用參數的優(yōu)點是:當對象很大或需要深層復制時,可大大減少對資源的占用,提高參數傳遞的效率。但無法利用系統(tǒng)的自動轉換機制。
5.作為成員函數重載還是作為非成員函數重載?
=、[ ]、()、->以及所有的類型轉換運算符只能作為成員函數重載。如果允許第一操作數不是同類對象,而是其他數據類型,則只能作為非成員函數重開車(如輸入輸出流運算符和就是這樣的情況)。若希望系統(tǒng)在必要時能夠利用只需一個實參的構造函數自動對第一操作數進行轉換,也應將該運算符作為非成員函數重載;此種情況下,運算符函數的參數應該是非引用參數,否則不能達到所希望的效果。其他情況下一般應作為成員函數重載。
北京 | 天津 | 上海 | 江蘇 | 山東 |
安徽 | 浙江 | 江西 | 福建 | 深圳 |
廣東 | 河北 | 湖南 | 廣西 | 河南 |
海南 | 湖北 | 四川 | 重慶 | 云南 |
貴州 | 西藏 | 新疆 | 陜西 | 山西 |
寧夏 | 甘肅 | 青海 | 遼寧 | 吉林 |
黑龍江 | 內蒙古 |