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