第12章-運(yùn)算符重載_第1頁(yè)
第12章-運(yùn)算符重載_第2頁(yè)
第12章-運(yùn)算符重載_第3頁(yè)
第12章-運(yùn)算符重載_第4頁(yè)
第12章-運(yùn)算符重載_第5頁(yè)
已閱讀5頁(yè),還剩71頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

第15章運(yùn)算符重載本章要點(diǎn):

教學(xué)目標(biāo)本章主要介紹類的靜態(tài)成員以及友元的問(wèn)題。掌握靜態(tài)數(shù)據(jù)成員和靜態(tài)成員函數(shù)的使用,友元函數(shù)和友元類的添加。教學(xué)重點(diǎn)和難點(diǎn)◆什么是運(yùn)算符重載◆一元運(yùn)算符的重載◆二元運(yùn)算符的重載◆通過(guò)友元函數(shù)實(shí)現(xiàn)重載◆輸出、輸入運(yùn)算符的重載在前面學(xué)習(xí)類和對(duì)象的時(shí)候,我們明確了這樣一個(gè)概念:定義一個(gè)類就是定義一種新類型。因此,對(duì)象和變量一樣,可以作為函數(shù)的參數(shù)傳遞,可以作為函數(shù)的返回值的類型,也可以說(shuō)明對(duì)象數(shù)組,甚至還可能有類類型的常量。在基本數(shù)據(jù)類型上,系統(tǒng)提供了許多預(yù)定義的運(yùn)算符,它們以一種簡(jiǎn)潔的方式工作。例如“+”運(yùn)算符:int x,y;y=x+y;表示兩個(gè)整數(shù)相加,很簡(jiǎn)潔。但是,兩個(gè)字符串合并:char x[20],y[10]; //定義兩個(gè)字符串類型//……strcat(x,y);表達(dá)起來(lái)就不如“y=x+y;”那樣直觀簡(jiǎn)潔。因此,為了表達(dá)上的方便,希望已預(yù)定義的運(yùn)算符,也可以在特定類的對(duì)象上以新的含義進(jìn)行解釋。如在string類對(duì)象x、y的環(huán)境下,運(yùn)算符“+”能被解釋為字符串x和y的合并。換言之,希望預(yù)定義運(yùn)算符能被重載,讓用戶自定義的類也可以使用。12.1明確目標(biāo):為什么要進(jìn)行運(yùn)算符重載C語(yǔ)言中有多種內(nèi)置的數(shù)據(jù)類型,例如int、float、double和char等等。對(duì)應(yīng)于每一種類型都有一系列的內(nèi)置運(yùn)算符,比如加法運(yùn)算符“+”和乘法運(yùn)算符“*”。就拿運(yùn)算符“+”來(lái)說(shuō)吧,它可以用于int值,也可以用于float,雖然使用相同的運(yùn)算符,但生成的代碼不同,因?yàn)檎麛?shù)和浮點(diǎn)數(shù)在內(nèi)存中的表示是不同的。這時(shí),“+”運(yùn)算符具有兩種不同的解釋(即實(shí)現(xiàn)代碼)。也就是說(shuō),像“+”這樣的運(yùn)算符在C語(yǔ)言中已被重載。但是,C語(yǔ)言僅支持少量有限的運(yùn)算符重載。C++語(yǔ)言對(duì)重載功能進(jìn)行了擴(kuò)充,也允許我們?cè)谧约憾x的類上添加運(yùn)算符,允許對(duì)已存在的預(yù)定義運(yùn)算符由我們?cè)诓煌纳舷挛闹凶龀霾煌慕忉?。比如,?jīng)過(guò)運(yùn)算符重載,我們可以直接對(duì)兩個(gè)字符串string類對(duì)象進(jìn)行加法運(yùn)算stringx,y;x=x+y;可以直接輸出復(fù)數(shù)類CComplex對(duì)象Ccomplexc;cout<<c;通過(guò)運(yùn)算符重載,程序代碼就顯得簡(jiǎn)潔明了,類對(duì)象的使用也方便了。這就是我們重載運(yùn)算符的目的。第一個(gè)基本動(dòng)作就這么簡(jiǎn)單,沒(méi)問(wèn)題吧?好了,如果第一關(guān)沒(méi)問(wèn)題,可以繼續(xù)前進(jìn)了。先看看下面這段話:因?yàn)楸緯?shū)針對(duì)C++的入門(mén)讀者,所以對(duì)一些深層次復(fù)雜的運(yùn)算符重載,比如new和delete的重載以及類型轉(zhuǎn)換等等不做介紹,以免倒了你的胃口。這里我們只介紹一下常用的運(yùn)算符重載的實(shí)現(xiàn),讓大家明白運(yùn)算符重載是怎么回事、如何實(shí)現(xiàn),就算是完成任務(wù)了。如果想進(jìn)一步學(xué)習(xí)復(fù)雜的高級(jí)的重載,可以參見(jiàn)別的參考書(shū)。運(yùn)算符重載函數(shù)可以作為類的成員函數(shù),也可以作為類的友元函數(shù)來(lái)實(shí)現(xiàn)。下面對(duì)這兩種不同的實(shí)現(xiàn)分別講述。通俗地說(shuō),重載運(yùn)算符就是在寫(xiě)函數(shù),用這個(gè)運(yùn)算符函數(shù)體對(duì)重載的運(yùn)算符的含義做出新的解釋。這里所解釋的含義與重載該運(yùn)算符的類有關(guān),比如,字符串類重載的加運(yùn)算符“+”,實(shí)際上是對(duì)兩個(gè)字符串進(jìn)行拼接。重載后的運(yùn)算符還是按這些運(yùn)算符的表達(dá)方式使用。例如,在一個(gè)string類中重載了運(yùn)算符“+”為兩個(gè)字符串的合并,我們可以這樣寫(xiě):strings1,s2;s1=s1+s2; //合并串s1和串s2,存放到新串s1中。為了把運(yùn)算符從頭到腳地看個(gè)明明白白,我們創(chuàng)建一個(gè)新類:計(jì)數(shù)器類CCounter,逐步地根據(jù)需求來(lái)完善此類,并進(jìn)一步地解釋為什么要重載運(yùn)算符以及如何來(lái)重載。例12.1創(chuàng)建類CCounter,類CCounter的一個(gè)對(duì)象可以用來(lái)在循環(huán)中計(jì)數(shù)(感到驚奇吧),而且這個(gè)類的對(duì)象還可以用在其它程序中以實(shí)現(xiàn)數(shù)字遞增、遞減或跟蹤一些值的地方。12.2以成員函數(shù)實(shí)現(xiàn)運(yùn)算符重載程序清單C12_01.cpp//類Counter的簡(jiǎn)單定義及實(shí)現(xiàn)#include<iostream>usingnamespacestd;classCCounter{public: CCounter():m_val(0){} //缺省構(gòu)造函數(shù),成員變量m_val初始化為0 ~CCounter(){} //什么事都不干的析構(gòu)函數(shù) //下面兩個(gè)函數(shù)是私有變量m_val的存取訪問(wèn)函數(shù) unsignedGetVal()const {returnm_val;} voidSetVal(unsignedx) {m_val=x;}private: unsignedm_val;};main(){ CCountercounter; cout<<"counter的值是:"<<counter.GetVal()<<endl;}輸出結(jié)果counter的值是:0程序分析:事實(shí)上,上面創(chuàng)建的CCounter類是一個(gè)“垃圾類”,沒(méi)有任何用處,只有惟一的一個(gè)成員變量m_val,而且被缺省的構(gòu)造函數(shù)初始化為零。用類CCounter創(chuàng)建的對(duì)象不能像整型變量那樣進(jìn)行自增和自減運(yùn)算,也不能進(jìn)行加法、賦值或者其它的算術(shù)操作。更惡劣的是,這個(gè)類創(chuàng)建的對(duì)象的輸出也是一個(gè)問(wèn)題,不能像整型數(shù)那樣直接用cout進(jìn)行輸出。通過(guò)我們即將要學(xué)習(xí)的運(yùn)算符重載來(lái)對(duì)CCounter類進(jìn)行“教化”,使它能夠像一般的整型數(shù)一樣來(lái)進(jìn)行一系列的算術(shù)運(yùn)算。再次說(shuō)明,我們只是想通過(guò)對(duì)類CCounter的逐步改造來(lái)講解運(yùn)算符重載的知識(shí),至于這個(gè)類本身,用處倒不大。12.2.1 重載一元運(yùn)算符:遞增運(yùn)算符“++”(1)通過(guò)添加成員函數(shù)Increment(),實(shí)現(xiàn)自增功能運(yùn)算符重載可以使類獲得使用這些運(yùn)算符的“權(quán)限”。例如,我們可以通過(guò)下面兩種方式讓類CCounter的對(duì)象具有自增運(yùn)算的功能。一種方式是通過(guò)在類CCounter中添加一個(gè)實(shí)現(xiàn)自增功能的方法Increment(),如下面例12.2:例12.2給CCounter類添加自增函數(shù)Increment()。程序清單C12_02.cpp#include<iostream>usingnamespacestd;classCCounter{public: CCounter():m_val(0){} ~CCounter(){} unsignedGetVal()const{returnm_val;} voidSetVal(unsignedx){m_val=x;} voidIncrement(){++m_val;}//自增函數(shù),將變量的值加1private: unsignedm_val;};main(){ CCountercounter; cout<<"counter的值是:"<<counter.GetVal()<<endl; counter.Increment(); cout<<"調(diào)用自增函數(shù)之后,"; cout<<"counter的值是:"<<counter.GetVal()<<endl;}輸出結(jié)果counter的值是:0調(diào)用自增函數(shù)之后,counter的值是:1程序分析:例12.2中,在類CCounter中添加了自增函數(shù)Increment(),雖然它可以正常工作,但是使用起來(lái)和運(yùn)算符“++”比較起來(lái)可是差遠(yuǎn)了,太麻煩了。似乎聽(tīng)見(jiàn)哪有抗議聲,哦,原來(lái)是上面的程序在喊:“為什么不給我添加++運(yùn)算符”。別急,當(dāng)然可以滿足它的愿望了,通過(guò)重載運(yùn)算符++就可以實(shí)現(xiàn)。(2)重載前綴運(yùn)算符“++”1) 重載前綴運(yùn)算符“++”,返回類型為void要重載前綴++運(yùn)算符,可以通過(guò)運(yùn)算符重載函數(shù)實(shí)現(xiàn),我們這里是把運(yùn)算符重載函數(shù)作為類的成員函數(shù)實(shí)現(xiàn)的,它的語(yǔ)法形式如下:其中,returntype是返回值類型,operator是關(guān)鍵字,“op”是要重載的運(yùn)算符符號(hào),classname是重載該運(yùn)算符的類的名稱。重載函數(shù)的函數(shù)名是由關(guān)鍵字operator后面加上要重載的運(yùn)算符符號(hào)組成的,為operatorop。這里我們用“op”泛指可被重載的運(yùn)算符。因此,自增運(yùn)算符++可以按如下的形式進(jìn)行重載:voidoperator++();returntypeclassname::operatorop(參數(shù)表){//相對(duì)于該類而定義的操作代碼}例12.3在CCounter類中通過(guò)運(yùn)算符++的重載,實(shí)現(xiàn)CCounter類的自增運(yùn)算++。程序清單C12_03.cpp//重載運(yùn)算符++,運(yùn)算符重載函數(shù)作為類CCounter的成員函數(shù)#include<iostream>usingnamespacestd;classCCounter{public: CCounter():m_val(0){} ~CCounter(){} unsignedGetVal()const{returnm_val;} voidSetVal(unsignedx){m_val=x;} voidIncrement(){++m_val;} voidoperator++(){++m_val;}//重載運(yùn)算符++private: unsignedm_val;};main(){ CCountercounter; cout<<"counter的值是:"<<counter.GetVal()<<endl; counter.Increment(); cout<<"調(diào)用自增函數(shù)之后,"; cout<<"counter的值是:"<<counter.GetVal()<<endl; ++counter; cout<<"使用自增運(yùn)算符++之后,"; cout<<"counter的值是:"<<counter.GetVal()<<endl;}輸出結(jié)果counter的值是:0調(diào)用自增函數(shù)之后,counter的值是:1使用自增運(yùn)算符++之后,counter的值是:2程序分析:在上面的程序中,函數(shù):voidoperator++(){++m_val;}實(shí)現(xiàn)了運(yùn)算符++的重載。在main()函數(shù)中,語(yǔ)句:++counter;對(duì)象counter,使用了類CCounter重載的運(yùn)算符++,這種語(yǔ)法形式與我們期望類CCounter對(duì)象所具有的形式非常接近。但是,或許你想讓類CCounter對(duì)象具有更多附加的功能,比如檢查CCounter類對(duì)象是否超出了最大值范圍等等。2) 重載運(yùn)算符“++”,返回一個(gè)CCounter類對(duì)象,通過(guò)創(chuàng)建臨時(shí)變量實(shí)現(xiàn)不知道你是否注意到,我們上面重載的自增運(yùn)算符有一個(gè)很大的缺陷,那就是如果你想把CCounter類對(duì)象放在賦值運(yùn)算符的右側(cè),那么對(duì)不起,編譯器會(huì)不客氣地報(bào)錯(cuò),你不妨試試。把CCounter類對(duì)象放在賦值運(yùn)算符的右側(cè),例如:CCountercounter2=++counter;這條語(yǔ)句試圖建立一個(gè)新的CCounter類對(duì)象counter2,然后把對(duì)象counter自增后的值賦給這個(gè)對(duì)象。內(nèi)置的拷貝構(gòu)造函數(shù)將處理賦值運(yùn)算,但是現(xiàn)在的自增運(yùn)算符并沒(méi)有返回一個(gè)CCounter類對(duì)象,而是返回空類型void,我們不能把一個(gè)空類型void對(duì)象賦給一個(gè)CCounter類對(duì)象。那怎么辦?下面我們就來(lái)解決這個(gè)棘手的問(wèn)題。顯然,我們只需要讓重載運(yùn)算符函數(shù)返回一個(gè)CCounter類對(duì)象問(wèn)題就解決了,這樣返回的對(duì)象值就可以賦給另一個(gè)CCounter類對(duì)象了。那么返回哪個(gè)對(duì)象呢?一種方法是創(chuàng)建一個(gè)臨時(shí)對(duì)象,然后返回。例12.4在CCounter類中重載運(yùn)算符++,并返回一個(gè)CCounter類型的臨時(shí)對(duì)象。程序清單C12_04.cpp//重載運(yùn)算符++,并返回一個(gè)臨時(shí)對(duì)象#include<iostream>usingnamespacestd;classCCounter{public: CCounter():m_val(0){} ~CCounter(){} unsignedGetVal()const{returnm_val;} voidSetVal(unsignedx){m_val=x;} voidIncrement(){++m_val;} CCounteroperator++();//重載++運(yùn)算符,返回值為CCounter類型private: unsignedm_val;};CCounterCCounter::operator++(){ ++m_val; CCountertemp; temp.SetVal(m_val); returntemp;}main(){ CCountercounter; cout<<"counter的值是:"<<counter.GetVal()<<endl; counter.Increment(); cout<<"調(diào)用自增函數(shù)之后,"; cout<<"counter的值是:"<<counter.GetVal()<<endl; ++counter; cout<<"使用自增運(yùn)算符++之后,"; cout<<"counter的值是:"<<counter.GetVal()<<endl; CCountercounter2=++counter; cout<<"counter2的值是:"<<counter2.GetVal()<<endl; cout<<"counter的值是:"<<counter.GetVal()<<endl;}輸出結(jié)果counter的值是:0調(diào)用自增函數(shù)之后,counter的值是:1使用自增運(yùn)算符++之后,counter的值是:2counter2的值是:3counter的值是:3程序分析:在上面這個(gè)版本的程序中,運(yùn)算符重載函數(shù)operator++的返回值為一個(gè)CCounter類對(duì)象。在函數(shù)operator++中,我們創(chuàng)建了一個(gè)臨時(shí)對(duì)象temp:CCountertemp;而且通過(guò)語(yǔ)句temp.SetVal(m_val);將對(duì)象temp的值設(shè)置為當(dāng)前對(duì)象的值。臨時(shí)對(duì)象被返回而且賦值給對(duì)象counter2。CCountercounter2=++counter;至此,上面的實(shí)現(xiàn)好像是很完美了,但是,有一個(gè)問(wèn)題,就是:為什么我們要?jiǎng)?chuàng)建一個(gè)臨時(shí)對(duì)象?”,記?。好總€(gè)臨時(shí)對(duì)象被創(chuàng)建而且使用完之后必須要被銷毀——這是一個(gè)潛在的“奢侈”的操作,要耗費(fèi)資源,況且對(duì)象counter已經(jīng)存在,并且已經(jīng)有正確的值,那么為什么不直接返回它呢?我們可以通過(guò)使用this指針來(lái)解決這個(gè)問(wèn)題。3)重載運(yùn)算符“++”,通過(guò)this指針?lè)祷匾粋€(gè)CCounter類對(duì)象的引用正如我們?cè)诘?3章中討論的那樣,this指針是類的所有成員函數(shù)的隱含參數(shù),因?yàn)檫\(yùn)算符重載函數(shù)operator++是類的成員函數(shù),所以this指針是該函數(shù)的隱含參數(shù)。this指針指向?qū)ο骳ounter,如果間接引用this指針(*this)則其返回對(duì)象counter,對(duì)象counter的成員變量m_val已經(jīng)有正確的值。例12.5在CCounter重載運(yùn)算符函數(shù)中返回間接引用this指針的值,這樣就可以避免創(chuàng)建一個(gè)臨時(shí)對(duì)象,從而可以提高程序運(yùn)行的效率。程序清單C12_05.CPP//返回this指針的間接引用#include<iostream>usingnamespacestd;classCCounter{public: CCounter():m_val(0){} ~CCounter(){} unsignedGetVal()const{returnm_val;} voidSetVal(unsignedx){m_val=x;} voidIncrement(){++m_val;} constCCounter&operator++();//重載++運(yùn)算符,返回this指針的間接引用private: unsignedm_val;};constCCounter&CCounter::operator++(){ ++m_val; return*this;}main(){ CCountercounter; cout<<"counter的值是:"<<counter.GetVal()<<endl; counter.Increment(); cout<<"調(diào)用自增函數(shù)之后,"; cout<<"counter的值是:"<<counter.GetVal()<<endl; ++counter; cout<<"使用自增運(yùn)算符++之后,"; cout<<"counter的值是:"<<counter.GetVal()<<endl; CCountercounter2=++counter; cout<<"counter2的值是:"<<counter2.GetVal()<<endl; cout<<"counter的值是:"<<counter.GetVal()<<endl;}輸出結(jié)果counter的值是:0調(diào)用自增函數(shù)之后,counter的值是:1使用自增運(yùn)算符++之后,counter的值是:2counter2的值是:3counter的值是:3程序分析:在上面的程序中,通過(guò)間接引用this指針,返回當(dāng)前對(duì)象的引用,并將當(dāng)前對(duì)象的值賦給對(duì)象counter2。注意:上面程序中返回的是當(dāng)前對(duì)象的引用,因此就避免了創(chuàng)建多余的臨時(shí)對(duì)象。因?yàn)橐乐巩?dāng)前的對(duì)象值被別的函數(shù)改變,所以返回值是const限定的常量引用(回憶一下const限定的使用)。好了,現(xiàn)在類CCounter已經(jīng)有了一個(gè)使用方便,性能卓越的++運(yùn)算符了。大家自然的會(huì)想到前面的++運(yùn)算符是前綴自增,那后綴自增運(yùn)算符又該如何重載呢?想的周到,值得表?yè)P(yáng)。我們下面就看看重載后綴自增運(yùn)算符如何實(shí)現(xiàn)。(3)重載后綴運(yùn)算符“++”首先來(lái)看看編譯器是如何區(qū)別前綴自增和后綴自增運(yùn)算的?按照約定,在運(yùn)算符重載函數(shù)的聲明中,提供一個(gè)整型數(shù)作為函數(shù)的參數(shù),就表示重載的是后綴運(yùn)算符。參數(shù)值并沒(méi)有什么用,它只是用來(lái)表明重載的是后綴運(yùn)算符。在我們重載后綴自增運(yùn)算符之前,先要徹底搞清楚到底它與前綴自增運(yùn)算符有什么不同。如果忘記了,請(qǐng)回頭去看看前面的C語(yǔ)言部分。前面我們講到,前綴自增是“先自增,然后再拿來(lái)參與運(yùn)算”,而后綴自增相反,它是“先拿來(lái)用,然后變量再自增”。因此,重載前綴運(yùn)算符只要簡(jiǎn)單地遞增變量的值,然后返回對(duì)象本身即可,而后綴運(yùn)算符必須返回進(jìn)行自增運(yùn)算之前的對(duì)象的值。為了做到這一點(diǎn),我們必須創(chuàng)建一個(gè)臨時(shí)對(duì)象,用它來(lái)存儲(chǔ)對(duì)象的初始值,然后增加對(duì)象的初始值,最后返回臨時(shí)對(duì)象。讓我們通過(guò)下面的小例子再簡(jiǎn)單地回顧一下后綴運(yùn)算符的使用。m=n++;如果n的值是6,執(zhí)行完上述語(yǔ)句之后,m的值是6,但是n的值是7。這樣,我們返回了變量n原來(lái)的值,并且賦給變量m,然后將n的值加1。如果n是一個(gè)對(duì)象,它的后綴自增運(yùn)算必須先將對(duì)象原來(lái)的值(如6)存儲(chǔ)到一個(gè)臨時(shí)對(duì)象中,然后增加n的值(n的值變?yōu)?),最后返回臨時(shí)對(duì)象的值并賦給變量m。注意:因?yàn)槲覀兎祷氐氖桥R時(shí)對(duì)象,所以必須返回對(duì)象的值,而不能是返回引用,這是因?yàn)榕R時(shí)對(duì)象在函數(shù)返回時(shí)就超出了它的作用范圍,將被銷毀,如果返回它的引用,那么這個(gè)引用將是一個(gè)已經(jīng)不存在的對(duì)象的引用,其后果是不可預(yù)料的。例12.6在CCounter中實(shí)現(xiàn)后綴和前綴自增運(yùn)算符的重載。程序清單C12_06.cpp//重載后綴和前綴自增運(yùn)算符#include<iostream>usingnamespacestd;classCCounter{public: CCounter():m_val(0){} ~CCounter(){} unsignedGetVal()const{returnm_val;} voidSetVal(unsignedx){m_val=x;} voidIncrement(){++m_val;} constCCounter&operator++(); //重載前綴++運(yùn)算符 constCCounteroperator++(int); /*重載后綴++運(yùn)算符,帶一個(gè)整型參數(shù), 表示是后綴運(yùn)算符*/private: unsignedm_val;};constCCounter&CCounter::operator++(){ ++m_val; return*this;}constCCounterCCounter::operator++(int){ CCountertemp; temp.m_val=m_val; //將對(duì)象原有的值保存到臨時(shí)對(duì)象中 ++m_val; //自增對(duì)象的值 returntemp; //返回臨時(shí)對(duì)象}main(){ CCountercounter; cout<<"counter的值是:"<<counter.GetVal()<<endl; counter.Increment(); cout<<"調(diào)用自增函數(shù)之后,"; cout<<"counter的值是:"<<counter.GetVal()<<endl; ++counter; cout<<"使用自增運(yùn)算符++之后,"; cout<<"counter的值是:"<<counter.GetVal()<<endl; CCountercounter2=++counter; cout<<"counter2的值是:"<<counter2.GetVal()<<endl; cout<<"counter的值是:"<<counter.GetVal()<<endl; counter2=counter++; cout<<"counter2的值是:"<<counter2.GetVal()<<endl; cout<<"counter的值是:"<<counter.GetVal()<<endl;}輸出結(jié)果counter的值是:0調(diào)用自增函數(shù)之后,counter的值是:1使用自增運(yùn)算符++之后,counter的值是:2counter2的值是:3counter的值是:3counter2的值是:3counter的值是:4在上面的程序中,分別實(shí)現(xiàn)了前綴和后綴運(yùn)算符的重載。注意,在main()函數(shù)中使用后綴++運(yùn)算符時(shí),并沒(méi)有在它的后面加一個(gè)整型的標(biāo)志,而是像其它內(nèi)置數(shù)據(jù)類型一樣的來(lái)使用后綴++運(yùn)算符。前面我們已經(jīng)強(qiáng)調(diào),在后綴運(yùn)算符重載函數(shù)中帶一個(gè)整型參數(shù),只是為了表明是后綴運(yùn)算符,其所帶的參數(shù)值從不被使用。至此,我們實(shí)現(xiàn)了前綴和后綴的++運(yùn)算符的重載。前綴和后綴的自減運(yùn)算符--的重載完全類似。你不妨自己在類CCounter中添加進(jìn)去(不要偷懶呦?。?。重載運(yùn)算符事實(shí)上就是在實(shí)現(xiàn)一個(gè)函數(shù),函數(shù)的函數(shù)名由關(guān)鍵字operator和要重載的運(yùn)算符組成。如果運(yùn)算符重載函數(shù)是類的成員函數(shù)(如上面的示例程序),那么重載一元運(yùn)算符的函數(shù)不帶參數(shù)。注意,如果重載的是后綴運(yùn)算符,如++或--,那么不要忘記帶一個(gè)整型參數(shù)作為標(biāo)志。例如:constCCounter&CCounter::operator++(); //前綴++CCounterCCounter::operator--(int); //后綴--12.2.2二元運(yùn)算符重載:重載加運(yùn)算符“+”前面我們講述了一元運(yùn)算符前綴自增和后綴自增的重載,它們只是在一個(gè)對(duì)象上操作。加法運(yùn)算符“+”是二元運(yùn)算符,是對(duì)兩個(gè)對(duì)象進(jìn)行操作。如何對(duì)類CCounter實(shí)現(xiàn)加運(yùn)算(+)的重載呢?我們的目標(biāo)是聲明兩個(gè)CCounter類對(duì)象,然后對(duì)它們進(jìn)行加運(yùn)算,例如:CCountercounter1,counter2,counter3;counter3=counter1+counter2;(1)添加成員函數(shù)Add(),實(shí)現(xiàn)對(duì)象相加的功能實(shí)現(xiàn)加法功能的第一種方式是:通過(guò)在類CCounter中添加一個(gè)成員函數(shù)Add(),該函數(shù)以一個(gè)CCounter類對(duì)象作為參數(shù),將對(duì)象參數(shù)的值和當(dāng)前對(duì)象的值相加,然后返回一個(gè)CCounter類對(duì)象。例12.7在CCounter類中添加Add()函數(shù),實(shí)現(xiàn)兩個(gè)CCounter對(duì)象相加。程序清單C12_07.cpp#include<iostream>usingnamespacestd;classCCounter{public: CCounter():m_val(0){} CCounter(unsignedinitVal):m_val(initVal){} ~CCounter(){} unsignedGetVal()const{returnm_val;} voidSetVal(unsignedx){m_val=x;} CCounterAdd(constCCounter&cnt);private: unsignedm_val;};CCounterCCounter::Add(constCCounter&cnt){ CCountertemp; temp.SetVal(m_val+cnt.GetVal()); returntemp;}main(){ CCountercounter1(1),counter2(3),counter3; //創(chuàng)建三個(gè)CCounter類對(duì)象 counter3=counter1.Add(counter2); //調(diào)用成員函數(shù)Add() cout<<"counter1的值是:"<<counter1.GetVal()<<endl; cout<<"counter2的值是:"<<counter2.GetVal()<<endl; cout<<"counter3=counter1+ounter2的值是:"<<counter3.GetVal()<<endl;}輸出結(jié)果counter1的值是:1counter2的值是:3counter3=counter1+counter2的值是:4(2)重載加運(yùn)算符“+”我們實(shí)現(xiàn)的Add()函數(shù)雖然可以正常工作了,但是使用起來(lái)不夠簡(jiǎn)單自然。不說(shuō)大家可能也會(huì)想到,我們實(shí)現(xiàn)兩個(gè)CCounter類對(duì)象進(jìn)行加運(yùn)算的另外一種方法是重載運(yùn)算符“+”,使加運(yùn)算可以寫(xiě)為如下形式:CCountercounter1,counter2,counter3;counter3=counter1+counter2;例12.8在CCounter類中實(shí)現(xiàn)重載運(yùn)算符“+”。程序清單C12_08.cpp//重載運(yùn)算符"+"#include<iostream>usingnamespacestd;classCCounter{public: CCounter():m_val(0){} CCounter(unsignedinitVal):m_val(initVal){} ~CCounter(){} unsignedGetVal()const{returnm_val;} voidSetVal(unsignedx){m_val=x;} CCounteroperator+(constCCounter&cnt); //重載加(+)運(yùn)算符private: unsignedm_val;};CCounterCCounter::operator+(constCCounter&cnt){ CCountertemp; temp.SetVal(m_val+cnt.GetVal()); returntemp;}main(){ CCountercounter1(2),counter2(4),counter3; //創(chuàng)建三個(gè)CCounter類對(duì)象 counter3=counter1+counter2; cout<<"counter1的值是:"<<counter1.GetVal()<<endl; cout<<"counter2的值是:"<<counter2.GetVal()<<endl; cout<<"counter3=counter1+counter2的值是:"<<counter3.GetVal()<<endl;}輸出結(jié)果counter1的值是:2counter2的值是:4counter3=counter1+counter2的值是:6程序分析:在上面的程序中,重載了加(+)運(yùn)算符。將加運(yùn)算符重載函數(shù)的聲明和實(shí)現(xiàn)分別與Add()函數(shù)的聲明和實(shí)現(xiàn),做個(gè)比較,它們幾乎是一模一樣,只是它們的調(diào)用語(yǔ)法不同,使用下面的調(diào)用方式(2)顯然要比方式(1)簡(jiǎn)單自然:(1)counter3=counter1.Add(counter2);(2)counter3=counter1+counter2;雖然沒(méi)有多大的變化,但已經(jīng)足以使程序易于理解,代碼書(shū)寫(xiě)也更方便了。注意:重載加運(yùn)算符的方法同樣適用于其它的二元運(yùn)算符,比如,減(-)操作等等。除了重載二元運(yùn)算符的函數(shù)帶有一個(gè)參數(shù)之外,其它的都和重載一元運(yùn)算符類似。重載運(yùn)算符函數(shù)的參數(shù)是對(duì)象的常值引用類型。例如:CounterCounter::operator+(constCounter&cnt);//重載+運(yùn)算符CounterCounter::operator-(constCounter&cnt);//重載-運(yùn)算符在上面的程序中,我們實(shí)現(xiàn)了二元運(yùn)算符“+”的重載,其它二元運(yùn)算符的重載類似,大家可以嘗試對(duì)類CCounter重載“-”運(yùn)算符。12.2.3 重載賦值運(yùn)算符“=”賦值運(yùn)算符“=”可以被重載,而且必須被重載為成員函數(shù),其重載格式為:其中A表示類名,source是一個(gè)A類型的對(duì)象。當(dāng)用戶在一個(gè)類中顯式地重載了賦值運(yùn)算符“=”時(shí),稱用戶定義了類賦值運(yùn)算。它將一個(gè)A類的對(duì)象source逐域拷貝(拷貝所有的成員)到賦值號(hào)左端的類對(duì)象中。如果用戶沒(méi)有為一個(gè)類重載賦值運(yùn)算符,編譯程序?qū)⑸梢粋€(gè)缺省的賦值運(yùn)算符。賦值運(yùn)算把源對(duì)象的值逐域地拷貝到目標(biāo)對(duì)象中。對(duì)許多簡(jiǎn)單的類,如前面的CCounter類,缺省的賦值函數(shù)工作的很好。AA::operator=(constAsource){//復(fù)制對(duì)象source的所有成員}但是,如果用戶定義的類中有指針成員,牽扯到內(nèi)存的分配問(wèn)題,那么系統(tǒng)提供的缺省的賦值運(yùn)算就不能正確工作,用戶必須顯式地為類定義賦值運(yùn)算。此問(wèn)題類似于我們?cè)诘?3章講到的拷貝構(gòu)造函數(shù)??截悩?gòu)造函數(shù)和賦值運(yùn)算符都是把一個(gè)對(duì)象的數(shù)據(jù)成員拷貝到另一個(gè)對(duì)象,它們的函數(shù)體實(shí)現(xiàn)非常類似,但是它們是有區(qū)別的??截悩?gòu)造函數(shù)是要以一個(gè)已存在的對(duì)象來(lái)創(chuàng)建一個(gè)新對(duì)象,而賦值運(yùn)算符則是改變一個(gè)已存在的對(duì)象的值。我們?cè)谇懊娴牟糠痔岬竭^(guò),如果我們自己不定義拷貝構(gòu)造函數(shù),那么系統(tǒng)會(huì)自動(dòng)提供一個(gè)。同樣,如果我們對(duì)自己定義的類,不定義賦值運(yùn)算,那么系統(tǒng)也會(huì)提供默認(rèn)的賦值運(yùn)算操作(operator=)。無(wú)論什么時(shí)候你使用對(duì)象進(jìn)行賦值時(shí),這個(gè)操作都會(huì)被調(diào)用。例如:CCatcat1(2,4),cat2(5,7);//其它程序代碼cat2=cat1;注:關(guān)于CCat類的定義,請(qǐng)看第13章的示例13.2。在上面這段代碼中,創(chuàng)建了兩個(gè)CCat類對(duì)象:cat1和cat2,并且把cat1的成員變量m_age和m_weight分別初始化為2和4,把cat2的成員變量m_age和m_weight分別初始化為5和7。在執(zhí)行過(guò)一些程序代碼之后,將對(duì)象cat1的值賦給cat2,別看這小小的一個(gè)舉動(dòng),卻會(huì)引發(fā)兩個(gè)潛在的問(wèn)題:首先,如果成員m_age是一個(gè)指針而不是整型變量,會(huì)導(dǎo)致什么情況發(fā)生?其次,對(duì)象cat2的原有的值會(huì)發(fā)生什么變化?在第13章我們討論拷貝構(gòu)造函數(shù)的時(shí)候,就提出了關(guān)于成員變量如果是指針類型的情況,對(duì)于賦值運(yùn)算有同樣的問(wèn)題。在C++的程序中,淺拷貝(逐域拷貝)不同于深拷貝,前者只是簡(jiǎn)單地拷貝對(duì)象的成員。如果類中有指針成員,那么如果使用淺拷貝將導(dǎo)致兩個(gè)對(duì)象將指向同一塊存儲(chǔ)空間;而深拷貝不同,它會(huì)另外分配一塊空間給目標(biāo)對(duì)象的指針變量。例12.9在CCat類中定義賦值運(yùn)算,即重載賦值運(yùn)算符。程序清單C12_09.cpp//重載賦值運(yùn)算符=,重載函數(shù)中包括預(yù)防自復(fù)制的代碼#include<iostream>#include<string>usingnamespacestd;classCCat{public: CCat(); //缺省構(gòu)造函數(shù) intGetAge()const{return*m_age;} char*GetName()const{returnm_name;} voidSetAge(intage){*m_age=age;} voidSetName(char*name); CCatoperator=(constCCat&); //重載賦值運(yùn)算符private: int*m_age; char*m_name;};CCat::CCat(){ m_age=newint; //給指針?lè)峙淇臻g *m_age=5; m_name=newchar[20]; //給Name指針?lè)峙淇臻g strcpy(m_name,"Mypet");}voidCCat::SetName(char*name){ delete[]m_name; m_name=newchar[strlen(name)+1]; strcpy(m_name,name);}CCatCCat::operator=(constCCat&sCat){ //如果源對(duì)象的地址和this指針相等,說(shuō)明它們是同一個(gè)對(duì)象,是自賦值 if(this==&sCat) return*this; delete[]m_name; //刪除原有的空間 m_name=newchar[strlen(sCat.m_name)+1]; //分配新的存儲(chǔ)空間 *m_age=sCat.GetAge(); strcpy(m_name,sCat.m_name); //將源對(duì)象的值賦給當(dāng)前對(duì)象 return*this; //返回當(dāng)前對(duì)象}main(){ CCatcat1; cat1.SetAge(2); cout<<"cat1的年齡是:"<<cat1.GetAge()<<endl <<"昵稱:"<<cat1.GetName()<<endl; CCatcat2; cat2.SetName("SunFlower"); cout<<"cat2的年齡是:"<<cat2.GetAge()<<endl <<"昵稱:"<<cat2.GetName()<<endl; cout<<"\n把cat1的值賦給cat2...\n\n"; cat2=cat1; cout<<"cat2的年齡是:"<<cat2.GetAge()<<endl <<"昵稱:"<<cat1.GetName()<<endl;}輸出結(jié)果cat1的年齡是:2昵稱:Mypetcat2的年齡是:5昵稱:SunFlower把cat1的值賦給cat2...cat2的年齡是:2昵稱:Mypet上面CCat類的定義可能把你都搞糊涂了,我想你肯定不理解為什么要把成員變量聲明為如下的形式:int*m_age;char*m_name;沒(méi)錯(cuò),這樣做是有點(diǎn)讓人費(fèi)解。之所以這樣做,目的只是為了說(shuō)明:當(dāng)成員變量中有指針時(shí),應(yīng)該如何重載賦值運(yùn)算符。我們?cè)谫x值運(yùn)算符的重載函數(shù)中,通過(guò)語(yǔ)句“if(this==&sCat)return*this;”檢查了自賦值的情況。這樣做的目的是處理由于當(dāng)對(duì)象cat2作為賦值運(yùn)算符的右操作數(shù)將它的值都拷貝到內(nèi)存中后(cat2=cat2;),接著它會(huì)釋放它占用的存儲(chǔ)空間(delete[]m_name;),所導(dǎo)致的一個(gè)非常大的問(wèn)題:存儲(chǔ)空間沒(méi)了。更重要的是,當(dāng)我們使用引用或者間接的引用指針時(shí)這種自賦值的情況很可能發(fā)生。比如:CCatcat2;//其它的程序代碼CCat&cat3=cat1;//其它的程序代碼cat2=cat3;//自賦值這樣通過(guò)重載賦值運(yùn)算符,很好地解決了我們上面提到的兩個(gè)問(wèn)題,一個(gè)是當(dāng)成員變量是指針的問(wèn)題,另一個(gè)是自賦值的問(wèn)題。12.3.1用友元函數(shù)重載加法運(yùn)算符“+”有些時(shí)候,用成員函數(shù)重載運(yùn)算符會(huì)碰到一些麻煩。例如:例12.10在復(fù)數(shù)類CComplex中用成員函數(shù)重載運(yùn)算符“+”。程序清單C12.10.cpp//用成員函數(shù)重載運(yùn)算符"+"classCComplex{public: CComplex(){real=0.0;image=0.0;} CComplex(doublerv){real=rv;image=0.0;} CComplex(doublerv,doubleiv){real=rv;image=iv;} CComplexoperator+(constCComplex&); //重載的加運(yùn)算符 ~CComplex(){};private: double real; //復(fù)數(shù)的實(shí)部 double image; //復(fù)數(shù)的虛部};12.3用友元函數(shù)重載運(yùn)算符CComplexCComplex::operator+(constCComplex&c){ CComplex temp; temp.real=real+c.real; temp.image=image+c.image; returntemp;}main(){ CComplexc1(1,5),c2(3); c1=c2+16; //正確:被編譯器解釋為c1=c2.operator+(CComplex(16)) c1=16+c2; //錯(cuò)誤:被解釋為:c1=16.operator+(c2);}在上面的程序段中,語(yǔ)句“c1=c2+16;”可被解釋為“c1=c2.operator+(16)”。c2是一個(gè)復(fù)數(shù)類CComplex對(duì)象,系統(tǒng)用的是復(fù)數(shù)類中重載的運(yùn)算符“+”。所需要的參數(shù)應(yīng)該是CComplex類對(duì)象,雖然上述語(yǔ)句中給出的參數(shù)是整數(shù)16,但是系統(tǒng)可以自動(dòng)的將16進(jìn)行類型轉(zhuǎn)換,它通過(guò)構(gòu)造函數(shù)“CComplex(doublerv){real=rv;image=0.0;}”,將整數(shù)16變?yōu)镃Complex類類型常量CComplex(16)。因此上述語(yǔ)句可以正常工作。但是,語(yǔ)句“c1=16+c2;”將被解釋為“c1=16.operator+(c2);”。這句不會(huì)逃過(guò)編譯器的考核,因?yàn)?6不是用戶定義的CComplex類對(duì)象,在程序的上下文中,編譯器并不知道用戶重載的運(yùn)算符“+”的含義,所以只好用系統(tǒng)預(yù)定義的“+”運(yùn)算符的含義進(jìn)行解釋,顯然,整數(shù)常量16不能與復(fù)數(shù)對(duì)象c2進(jìn)行加法,所以這個(gè)語(yǔ)句不能正常工作。在上述代碼中,我們把“+”運(yùn)算符作為CComplex類的成員函數(shù)進(jìn)行重載,不具有交換性。這是因?yàn)樽鳛槌蓡T函數(shù)重載的運(yùn)算符“+”僅能通過(guò)實(shí)際的對(duì)象所調(diào)用。如果引起調(diào)用的是一個(gè)非CComplex類對(duì)象的值,比如上面的整型數(shù)16,則該成員函數(shù)就不知何去何從了。那我們?cè)撊绾巫霾拍茏尅?”運(yùn)算符恢復(fù)它的靈活性,具有可交換性呢?C++語(yǔ)言的開(kāi)發(fā)者,當(dāng)然替我們想得很周到了,提供了“靈丹妙藥”來(lái)解決此問(wèn)題了。這就是將運(yùn)算符重載函數(shù)作為友元函數(shù)來(lái)實(shí)現(xiàn)。因?yàn)橛言瘮?shù)沒(méi)有隱含的this指針,所以用友元函數(shù)實(shí)現(xiàn)運(yùn)算符重載時(shí),該運(yùn)算符的操作數(shù)都必須在友元函數(shù)的參數(shù)表中明確聲明。修改例12.10的程序,將“+”運(yùn)算符的重載函數(shù)作為類CComplex的友元函數(shù)來(lái)實(shí)現(xiàn)。具體實(shí)現(xiàn)代碼如下:例12.11在CComplex類中使用友元函數(shù)重載運(yùn)算符。程序清單C12.11.cpp//鏈表類CLinkList的使用//用友元函數(shù)重載運(yùn)算符"+"classCComplex{public: CComplex(){real=0.0;image=0.0;} CComplex(doublerv){real=rv;image=0.0;} CComplex(doublerv,doubleiv){real=rv;image=iv;} friendCComplexoperator+(CComplexc1,CComplexc2); //作為類的友元函數(shù),重載加運(yùn)算符。 ~CComplex(){}private: double real; //復(fù)數(shù)的實(shí)部 double image; //復(fù)數(shù)的虛部};CComplexoperator+(CComplexc1,CComplexc2){ CComplex temp; temp.real=c1.real+c2.real; temp.image=c1.image+c2.image; returntemp;}main(){ CComplexc1(1,5),c2(3); c1=c2+16; //正確:被編譯器解釋為c1=operator+(c2,CComplex(16)) c1=16+c2; //正確:被編譯器解釋為c1=operator+(CComplex(16),c2)}這次就不會(huì)受編譯器的“責(zé)難”了,可以順利地通過(guò)編譯。那么什么時(shí)候使用成員函數(shù)實(shí)現(xiàn)重載,什么時(shí)候又用友元函數(shù)實(shí)現(xiàn)呢?有些運(yùn)算符必須用成員函數(shù)重載,比如賦值運(yùn)算符“=”、取下標(biāo)運(yùn)算符“[]”、函數(shù)調(diào)用運(yùn)算符“()”以及間接指針運(yùn)算符“->”。但是有些時(shí)候,比如例12.10中的情況,我們只能通過(guò)友元函數(shù)重載來(lái)實(shí)現(xiàn),還有輸出運(yùn)算符“<<”也只能通過(guò)友元函數(shù)來(lái)重載(原因在講解例12.12時(shí)會(huì)說(shuō)明)。總之,二者選擇其一,要視具體情況而定。下面給出作為成員函數(shù)或是友元函數(shù)來(lái)實(shí)現(xiàn)運(yùn)算符重載的不同之處:1)因?yàn)槌蓡T函數(shù)有隱含的this指針,所以在用成員函數(shù)重載運(yùn)算符時(shí),this指針可以作為一個(gè)參數(shù)使用。這樣,如果重載的運(yùn)算符是一元運(yùn)算符,則運(yùn)算符重載函數(shù)不需要參數(shù),因?yàn)閠his指針?biāo)傅膶?duì)象就是運(yùn)算符的操作數(shù);如果重載的是二元運(yùn)算符,那么運(yùn)算符重載函數(shù)只需要帶一個(gè)參數(shù),其中參數(shù)表示運(yùn)算符的右操作數(shù),this指針?biāo)傅膶?duì)象則是運(yùn)算符的左操作數(shù)。形式如下://成員函數(shù)形式,重載一元運(yùn)算符返回類型C::operatorop(){ //類C對(duì)op的解釋}//成員函數(shù)形式,重載二元運(yùn)算符返回類型C::operatorop(C&b) { //類C對(duì)op的解釋}其中,C是類名,op是要重載的運(yùn)算符,b是參數(shù)。2)因?yàn)橛言瘮?shù)沒(méi)有隱含的this指針,所以用友元函數(shù)實(shí)現(xiàn)運(yùn)算符重載時(shí),該運(yùn)算符的操作數(shù)都必須在友元函數(shù)的參數(shù)表中明確聲明。如果重載的運(yùn)算符是一元運(yùn)算符,則運(yùn)算符重載函數(shù)就要帶一個(gè)參數(shù),表示操作數(shù);如果重載的是二元運(yùn)算符,那么運(yùn)算符重載函數(shù)就要帶兩個(gè)參數(shù),分別表示兩個(gè)操作數(shù)。形式如下://作為類C的友員函數(shù),重載一元運(yùn)算符返回類型operatorop(C&a){ //對(duì)op的解釋}//作為類C的友員函數(shù),重載二元運(yùn)算符返回類型operatorop(C&a,C&b){ //對(duì)op的解釋}其中,C是類名,op是要重載的運(yùn)算符,a、b是參數(shù)。12.3.2重載輸出運(yùn)算符“<<”下面我們?cè)賮?lái)看一個(gè)使用友元函數(shù)實(shí)現(xiàn)重載輸出運(yùn)算符“<<”的例子。例12.12在復(fù)數(shù)類CComplex中重載輸出運(yùn)算符“<<”。程序清單C12_12.cpp//重載輸出運(yùn)算符"<<"#include<iostream.h> //有些編譯系統(tǒng)可能是包含iostream,并指明名字空間std;classCComplex{public: CComplex(){real=0.0;image=0.0;} CComplex(doublerv){real=rv;image=0.0;} CComplex(doublerv,doubleiv){real=rv;image=iv;} friendCComplexoperator+(CComplexc1,CComplexc2); //作為類的友元函數(shù),重載加運(yùn)算符 friendostream&operator<<(ostream&stream,CComplexc); //重載輸出運(yùn)算符"<<" ~CComplex(){};private: double real; //復(fù)數(shù)的實(shí)部 double image; //復(fù)數(shù)的虛部};CComplexoperator+(CComplexc1,CComplexc2){ CComplex temp; temp.real=c1.real+c2.real; temp.image=c1.image+c2.image; returntemp;}ostream&operator<<(ostream&stream,CComplexc){ stream<<“(”<<c.real<<“+”<<c.image<<“i)”<<endl; //以(a+bi)的格式輸出復(fù)數(shù) returnstream;}main(){ CComplex c1(1,5),c2(3); cout<<"c1="<<c1; //使用重載輸出運(yùn)算符"<<",輸出復(fù)數(shù)c1 cout<<"c2="<<c2; //使用重載輸出運(yùn)算符"<<",輸出復(fù)數(shù)c2 c1=c2+16; cout<<"執(zhí)行語(yǔ)句c1=c2+16;之后,"; cout<<"c1="<<c1;}輸出結(jié)果c1=(1+5i)c2=(3+0i)執(zhí)行語(yǔ)句c1=c2+16;之后,c1=(19+0i)運(yùn)算符重載的限制:重載運(yùn)算符時(shí),不能改變它們的優(yōu)先級(jí),也不能改變它們的結(jié)合性。當(dāng)然如果運(yùn)算符是一元的,我們不能把它重載為二元運(yùn)算符。也就是說(shuō),我們不能改變運(yùn)算符所需要的操作數(shù)的數(shù)目,更不能創(chuàng)造出新的運(yùn)算符。比如,不能聲明運(yùn)算符“**”來(lái)表示求操作數(shù)的平方。如果這樣,就違反了我們重載運(yùn)算符的初衷。進(jìn)行運(yùn)算符重載要注意的問(wèn)題:在C++的新手的編程工作中,運(yùn)算符重載是最容易被濫用的C++特性之一。因?yàn)閷?duì)一些含義模糊的運(yùn)算符進(jìn)行重載,使其具有有意義的功能,這是非常有誘惑力的。但是,盲目地重載運(yùn)算符會(huì)導(dǎo)致代碼混亂,難于理解。毫無(wú)疑問(wèn),通過(guò)重載,可以使運(yùn)算符“+”實(shí)現(xiàn)減法的功能,而使運(yùn)算符“*”用來(lái)進(jìn)行加法,這些也是非常有趣的事情,但是沒(méi)有專業(yè)的程序員會(huì)這么干。這樣做的最大的危險(xiǎn)是在于企圖是好的,但是卻使運(yùn)算符變了“質(zhì)”。一定要記住,重載運(yùn)算符的目的是增強(qiáng)代碼的可理解性,可不要背道而馳,異想天開(kāi)地盲目地重載運(yùn)算符。注意:只有在重載運(yùn)算符有益于代碼清晰的情況下,再實(shí)行重載。不要竄改運(yùn)算符原有的意義。在前面學(xué)習(xí)類和對(duì)象的時(shí)候,我們明確了這樣一個(gè)概念:定義一個(gè)類就是定義一種新類型。因此,對(duì)象和變量一樣,可以作為函數(shù)的參數(shù)傳遞,可以作為函數(shù)的返回值的類型,也可以說(shuō)明對(duì)象數(shù)組,甚至還可能有類類型的常量。在基本數(shù)據(jù)類型上,系統(tǒng)提供了許多預(yù)定義的運(yùn)算符,它們以一種簡(jiǎn)潔的方式工作。例如“+”運(yùn)算符:int x,y;y=x+y;表示兩個(gè)整數(shù)相加,很簡(jiǎn)潔。但是,兩個(gè)字符串合并:char x[20],y[10]; //定義兩個(gè)字符串類型//……strcat(x,y);表達(dá)起來(lái)就不如“y=x+y;”那樣直觀簡(jiǎn)潔。因此,為了表達(dá)上的方便,希望已預(yù)定義的運(yùn)算符,也可以在特定類的對(duì)象上以新的含義進(jìn)行解釋。如在string類對(duì)象x、y的環(huán)境下,運(yùn)算符“+”能被解釋為字符串x和y的合并。換言之,希望預(yù)定義運(yùn)算符能被重載,讓用戶自定義的類也可以使用。12.4應(yīng)用舉例例12.13修改類CPeople的實(shí)現(xiàn),重載類的輸出運(yùn)算符和賦值運(yùn)算符。程序清單People.h//類CPeople的定義#include<iostream>usingnamespacestd;classCPeople{ intm_nAge; floatm_fSalary;public: char*m_pstrName; CPeople(); ~CPeople(); CPeople(intage,floatsalary,char*name); floatGetSalary()const; voidSetSalary(float); intGetAge()const; voidSetAge(intage); CPeople&operator=(constCPeople&AnotherPeople); friendostream&operator<<(ostream&stream,constCPeople&p);};程序清單People.cpp//類CPeople的實(shí)現(xiàn)#include"People.h"#include<string>usingnamespacestd;CPeople::CPeople(){ m_nAge=20; m_fSalary=3000.00f; m_pstrName=newchar[20]; strcpy(m_pstrName,"無(wú)名氏");}CPeople::CPeople(intage,floatsalary,char*name){ m_nAge=age; m_fSalary=salary; m_pstrName=newchar[20]; strcpy(m_pstrName,name);}//類CPeople的實(shí)現(xiàn)#include"People.h"#include<string>usingnamespacestd;CPeople::CPeople(){ m_nAge=20; m_fSalary=3000.00f; m_pstrName=newchar[20]; strcpy(m_pstrName,"無(wú)名氏");}CPeople::CPeople(intage,floatsalary,char*name){ m_nAge=age; m_fSalary=salary; m_pstrName=newchar[20]; strcpy(m_pstrName,name);}CPeople::~CPeople(){ if(m_pstrName!=NULL) delete[]m_pstrName;}intCPeople::GetAge()const{ returnm_nAge;}voidCPeople::SetAge(intage){ m_nAge=age;}floatCPeople::GetSalary()const{ if(m_nAge<20) return0; elseif(GetAge()>60) returnm_fSalary/2; else returnm_fSalary;}voidCPeople::SetSalary(floatnum){ m_fSalary=num;}//賦值運(yùn)算符重載的實(shí)現(xiàn)CPeople&CPeople::operator=(constCPeople&AnotherPeople){ if(this==&AnotherPeople) //檢查自賦值 return*this; if(m_pstrName) delete[]m_pstrName; //釋放原有的內(nèi)存資源 m_nAge=AnotherPeople.m_nAge; m_fSalary=AnotherPeople.m_fSalary; //分配新的內(nèi)存資源,并復(fù)制內(nèi)容 m_pstrName=newchar[strlen(AnotherPeople.m_pstrName)+1]; strcpy(m_pstrName,AnotherPeople.m_pstrName); return*this;}//輸出運(yùn)算符重載函數(shù)的實(shí)現(xiàn)ostream&operator<<(ostream&stream,constCPeople&p){ stream<<"姓名:"<<p.m_pstrName<<"," <<"年齡:"<<p.GetAge()<<"," <<"薪水:"<<p.GetSalary(); returnstream;}程序清單C12_13.cpp//測(cè)試CPeople類#include<iostream>#include"people.h"usingnamespacestd;main(){ CPeopleZhang(65,2000.00f,"張飛"); cout<<Zhang<<endl; //輸出對(duì)象的值 CPeopleA,B; A.SetAge(34); A.SetSalary(4000.00f); cout<<A<<endl; B=A; //將對(duì)象A的值賦給對(duì)象B cout<<B<<endl;}輸出結(jié)果姓名:張飛,年齡:65,薪水:1000姓名:無(wú)名氏,年齡:34,薪水:4000姓名:無(wú)名氏,年齡:34,薪水:4000程序分析在上面的程序中,我們重新定義了類CPeople,重載了賦值運(yùn)算符“=”和輸出運(yùn)算符“<<”。重載賦值運(yùn)算符的函數(shù)體實(shí)現(xiàn)與拷貝構(gòu)造函數(shù)的實(shí)現(xiàn)非常相似(參照例13.12)。在賦值運(yùn)算符重載函數(shù)中,我們進(jìn)行了自賦值檢查,從而確保類對(duì)象的賦值運(yùn)算可以正常工作。重載了CPeople類的輸出運(yùn)算符,使對(duì)象的輸出變得簡(jiǎn)單好用。例12.14在復(fù)數(shù)類CComplex中實(shí)現(xiàn)=、+、-、++、<<運(yùn)算符的。程序清單complex.h#include<iostream.h>classCComplex{public: CComplex(){real=0.0;image=0.0;} CComplex(doublerv){real=rv;image=0.0;} CComplex(doublerv,doubleiv){real=rv;image=iv;} ~CComplex(){} CComplex&operator=(constCComplex&); CComplexoperator+(constCComplex&); CComplexoperator-(constCComplex&); CComplex&operator++(); //前綴 CComplexoperator++(int); //后綴 friendostream&operator<<(ostream&stream,CComplexc);private: double real; double image;};程序清單complex.cpp#include<iostream.h>#include"complex.h"CComplex&CComplex::operator=(constCComplex&c){ //判斷是否自復(fù)制 if(this==&c) return*this; real=c.real; image=c.image; return*this;}CComplexCComplex::operator+(constCComplex&c){ CComplex temp; temp.real=real+c.real; temp.image=image+c.image; returntemp;}CComplexCComplex::operator-(constCComplex&c){ CComplex temp; temp.real=this->real-c.real; temp.image=this->image-c.image; returntemp;}CComplex&CComplex::operator++() //前綴{ real++; image++; return*this;}CComplexCComplex::operator++(inta) //后綴{ CComplextemp; temp.real=real; temp.image=image; real++; image++; returntemp;}ostream&operator<<(ostream&stream,CComplexc){ cout<<c.image; stream<<"("<<c.real<<"+"<<c.image<<"i)"; //以格式(a+bi)顯示 returnstream;}程序清單C12_14.cpp#include<iostream.h>#include"complex.h"main(){ CComplexz(2.0,3.0),k(3.0,4.0),x(1.0),y; cout<<"z="<<z<<","<<"k="<<k<<endl; cout<<"x="<<x<<","<<"y="<<y<<endl; k++; x=z-k; y=z+x++; cout<<"經(jīng)過(guò)如下計(jì)算:\nk++\n"<<"x=z-k\n"<<"y=z+x++之后\n"; cout<<"===================="<<endl; cout<<"z="<<z<<","<<"k="<<k<<endl; cout<<"x="<<x<<","<<"y="<<y<<endl;}輸出結(jié)果z=(2+3i),k=(3+4i)x=(1+0i),

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論