《C++程序設計教程》課件第5章_第1頁
《C++程序設計教程》課件第5章_第2頁
《C++程序設計教程》課件第5章_第3頁
《C++程序設計教程》課件第5章_第4頁
《C++程序設計教程》課件第5章_第5頁
已閱讀5頁,還剩146頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權,請進行舉報或認領

文檔簡介

5.1概念

5.2重載函數(shù)作為成員函數(shù)的情況5.3重載函數(shù)作為友元函數(shù)的情況5.4重載賦值運算符

5.5拷貝構造函數(shù)

5.6類型的轉換

本章要點

練習

5.1.1運算符的重載

運算符的重載是通過編寫函數(shù)來實現(xiàn)的。函數(shù)名必須由關鍵字operator和緊跟其后的被重載的運算符組成。例如,若要重載“+”運算符,則重載函數(shù)名為operator+;要重載“[]”運算符,則重載函數(shù)名為operator[]。

有了函數(shù)名,要編寫一個函數(shù)來實現(xiàn)我們要求的運算,還需要有函數(shù)類型和函數(shù)參數(shù)。運算符有雙目運算符和單目運算符。根據(jù)重載函數(shù)的性質來分,有重載函數(shù)是成員函數(shù)和重載函數(shù)是友元函數(shù)兩類。根據(jù)被重載的運算符來分,有雙目運算符重載和單目運算符重載。5.1概念5.1.2運算符重載的限制

C++中提供的運算符重載機制,使程序更清晰,提高了可讀性。但C++對運算符重載作了一些限制。

(1)在運算符重載時不能改變運算符原來的優(yōu)先級、結合性和操作個數(shù)等性質。

(2)運算符=、()、[]和->的重載函數(shù),可作為類的成員函數(shù),但不可作為友元函數(shù)。

(3)運算符重載函數(shù)的參數(shù)不能為空。

(4)當參加運算的對象都屬內(nèi)部類型時不能重載。

(5)對可重載的運算符作了限制,如表5-1所示。表5-1運算符表5.2.1雙目運算符的重載

雙目運算符也稱二元運算符,如+、-等。當重載函數(shù)作為類的成員函數(shù)時,該函數(shù)可以少用一個參數(shù),因為成員函數(shù)隱藏了第一個參數(shù)——this指針。利用this指針就可完成當前對象與重載函數(shù)中給出的對象之間的操作。5.2重載函數(shù)作為成員函數(shù)的情況重載“+”運算符的方法是:Typeoperator-(Types),意為當出現(xiàn)當前類的對象與函數(shù)參數(shù)給出的對象進行“-”運算時就調(diào)用該函數(shù)。返回類型對該函數(shù)是否被調(diào)用沒有影響,理論上返回類型可以是任何類型,但一般總是設定為操作對象的類型。

對于上面的operator-函數(shù)調(diào)用,可理解為程序中有d1-d2的操作,“-”的左邊是當前類的對象,右邊是type的對象,于是條件滿足,就調(diào)用該函數(shù)。實際上這是在編譯時就決定了的,編譯器讀到d1-d2就將它理解為:this.operator-(d2)。

雙目運算符-的左邊是當前對象,右邊是函數(shù)參數(shù)給出的對象。所作的運算是,當前對象“-”函數(shù)參數(shù)給出的對象。因此要注意先后次序。

【例5-1】將兩個人民幣對象相減,再賦給另一人民幣對象。由于相減之后還要賦值,因此要求重載函數(shù)返回值。

#include<iostream>

usingnamespacestd;

classRMB

{public:voidmain()

{RMBa(2,50);RMBb(1,60);RMBc;

c=a-b; //將a減去b然后再賦給c

//c=a.operator-(b); //顯式調(diào)用,與上句等價

c.Display();a.Display();b.Display();

}

運行結果:

0.9

2.5

1.6從函數(shù)RMBoperator-(RMBs)的參數(shù)中注意到,雙目運算符重載函數(shù)是成員函數(shù)時,參數(shù)只用一個,因為operator-是成員函數(shù),它有指向當前對象的this指針。本例中,也可以將operator-的參數(shù)改為引用(RMB&s)。

【例5-2】也可以將例5-1中的operator-函數(shù)改為如下形式,其他都不變。RMBRMB::operator-(RMBs) //在其中完成圓值和角分

值的分別相減

{jf-=s.jf;

//注意:改變了當前對象的值

yuan-=s.yuan;

while(jf<0){yuan--;jf+=100;}

return(*this);

}

運行結果:

0.9

0.9

1.6在上面的operator-函數(shù)中,實現(xiàn)了當前對象與s對象的相減,結果放在當前對象中,返回當前對象的值。該方法改變了當前對象的值,在執(zhí)行了c=a-b后,a的值也改變了。

我們也可以編寫一個成員函數(shù)來實現(xiàn)上述功能。例如:將聲明中的RMBoperator-(RMB);改為RMBSub(RMB);,將實現(xiàn)中的RMBRMB::operator-(RMBs)改為RMBRMB::Sub(RMBs),再將c=a-b;改為c=a.Sub(b);便可。比較運算符重載和函數(shù)調(diào)用可以發(fā)現(xiàn),用運算符重載函數(shù)直觀方便,可讀性好。5.2.2單目運算符的重載

單目運算符也稱一元運算符,如++、--等。由于它們還有先后之分,因此重載時也分為重載先增量(減量)運算符和重載后增量(減量)運算符函數(shù)。若出現(xiàn)同樣的operator++或operator--函數(shù)名,編譯器就無法區(qū)分“先”和“后”操作函數(shù)。

C++中用函數(shù)參數(shù)來區(qū)分“先”和“后”操作函數(shù)。在C++中專門為“后”操作函數(shù)提供一個參數(shù)int,只給出類型沒有變量,別無它意,只為區(qū)分“后”函數(shù)。

重載先增量運算符的方法是:Typeoperator++(),意為一旦發(fā)生當前類的對象進行先增量運算就調(diào)用該函數(shù)。

重載后增量運算符的方法是:Typeoperator++(int),意為使用當前類的對象進行后增量運算時就調(diào)用該函數(shù)。

【例5-3】演示先增量和后增量運算符重載函數(shù)作為類的成員函數(shù)。運行結果:

50

52

【例5-4】若要實現(xiàn)增量之后再賦值,如d1=++d2,那么重載運算符函數(shù)就要有返回值。于是將例5-3修改如下:

#include<iostream>

usingnamespacestd;

classRMB運行結果:

51

51

52

先增量函數(shù)是先增值后返回,后增量函數(shù)也增值,但是它所要得到的是改變之前的值,所以要申請一個臨時對象暫存原值,然后再返回該臨時對象。5.3.1雙目運算符的重載

要通過重載的運算符函數(shù)對類進行操作,訪問類的保護成員或私有成員,必須將其聲明為類的友元。

雙目運算符的操作數(shù)既可以是兩個不同類型的對象(如對象與內(nèi)部類型變量或常數(shù)),也可以是兩個相同類型的對象。

【例5-5】人民幣有圓、角和分。要求實現(xiàn)人民幣對象的加操作,使其自動實現(xiàn)正確的操作。5.3重載函數(shù)作為友元函數(shù)的情況voidmain()

{RMBd1(4,50);RMBd2(6,68);RMBd3(0,0);

d3=d1+d2; //調(diào)用operator+(RMB&s1,RMB&s2)

d3.Display();

}

運行結果:

¥11.18注意operator+(RMB&s1,RMB&s2)函數(shù)的調(diào)用時機,當程序中有“d1+d2;”的操作且在“+”的左邊和右邊都是RMB對象時,條件滿足,就調(diào)用該函數(shù)。實際上這是在編譯時就決定了的,編譯器讀到“d1+d2;”就將它理解為operator+(d1,d2)。

在operator+函數(shù)中完成對應的圓加圓,角分加角分,然后以新得到的圓和角分值構造一個新的對象,在構造函數(shù)中進行修正。在本例中要實現(xiàn)d3=d1+d2,于是就將operator+函數(shù)的類型聲明為RMB。一般來說,把函數(shù)的參數(shù)設置為同類對象的引用,還可以提高執(zhí)行速度。如friendRMBoperator+(RMB&,RMB&);就可以發(fā)揮出引用的長處,而且這是常用方法。

若感到運算符的重載不夠清晰,可用函數(shù)調(diào)用的方法實現(xiàn)兩個用戶自定義對象的操作。我們還舉上面“+”的例子。例如,在類中聲明friendTypeAdd(Type1a,Type2b);,在類的實現(xiàn)中完成函數(shù)體函數(shù)定義,在程序中用Add(a,b);調(diào)用,就可以完成同樣的功能。

【例5-6】通過添加友元函數(shù)Add完成兩個RMB對象的相加。運行結果:

¥11.18

由例5-6得到了與例5-5同樣的結果,但顯然這兩種方法的效果不同。用函數(shù)就需要人為地調(diào)用語句,用重載運算符就不同了,當條件滿足時能自動調(diào)用重載函數(shù)。重載運算符的可讀性好,直觀方便。

【例5-7】通過完成兩個復數(shù)的加和減操作,演示如何重載“+=”運算符。運行結果:

a+b=4+6i

a=3+4i

b-a=-2-2i

b=1+2i

(b+=a)=1+2i

對重載運算符來說,若重載了“+”和“=”號并不等于有了“+=”的重載,因重載運算符并不具有結合性。在本例的重載運算符函數(shù)是傳值調(diào)用,是在副本上進行的操作,因此建立了一個臨時對象temp暫存操作結果,然后再將temp返回。正因為是返回對象,因此可用(a+b).desplay();等來輸出結果。

【例5-8】重載流插入<<和流提取>>運算符,完成輸入電話號碼然后再輸出的功能。運行結果:

Enteraphonenumberinthefrom456_87890649

0512_99208518↙

Thephonenumberenteredwas:

(0512)-992085185.3.2單目運算符的重載

對于單目運算符的重載,C++規(guī)定,若為先操作則返回引用,若為后操作則返回值。這是因為,在后操作中既要改變對象值,又要得到改變前的對象值,于是便生成一個新對象來保存操作前的對象,所以返回值;在先操作中直接作用于原對象本身,并不需要生成一個新對象來存放結果,所以返回引用。先后操作的意義在于決定了其不同的返回方式。

對于函數(shù),參數(shù)要求設置為引用,因為增減操作往往作用于原對象,而不是它的副本。

【例5-9】演示++運算符的重載。運行結果為:

thevalueis20

thevalueis21

thevalueis22

thevalueis23

thevalueis23

thevalueis23

thevalueis24本例中的語句friendIncreaseoperator++(Increase&,int);中的int僅作為后操作的標識。在++運算符重載函數(shù)的聲明中先加返回引用,后加返回值。由于(n++).Display();是后加,因此匹配的是Increaseoperator++(Increase&a,int)函數(shù)。該函數(shù)用當前對象構造了一個臨時對象temp,然后將當前對象增值,最后返回臨時對象temp,因此輸出仍為23。接著n.Display();輸出24。系統(tǒng)對C++中的賦值運算符已有了默認的操作方式,如d=3;是賦值運算符的應用。若有兩個對象a、b也進行a=b;操作,結果如何呢?看下面的例子。

【例5-10】現(xiàn)仍以復數(shù)操作為例,直接通過“=”來相互賦值。

#include<iostream>

usingnamespacestd;

classComplex

{public:5.4重載賦值運算符運行結果:

b=3+4i

在程序中并沒有重載“=”運算符,但它也實現(xiàn)了“賦值”,而且能自動完成實部和虛部分別“賦值”。C++的編譯器對于這種情況,首先檢查有無重載的賦值運算符函數(shù),若無則調(diào)用系統(tǒng)提供的默認拷貝構造函數(shù),進行簡單(淺)拷貝。一般情況下賦值運算符不需要重載,但是下面演示的情況卻有所不同。

【例5-11】分析用賦值運算符對具有申請資源的對象賦值時存在的缺陷。運算結果:出現(xiàn)錯誤。

出現(xiàn)錯誤的原因是兩個對象的數(shù)據(jù)成員str指向了同一個內(nèi)存地址。賦值運算只復制指針,不復制指針所指向單元內(nèi)的值(資源),結果兩個指針指向了同一個內(nèi)存地址。執(zhí)行程序結束時自動調(diào)用析構函數(shù),釋放了通過new分配的數(shù)據(jù)成員str指向的內(nèi)存,當?shù)诙€對象調(diào)用析構函數(shù)時試圖再次釋放同一內(nèi)存,于是就發(fā)生了錯誤。解決問題的方法是重載賦值運算符。重載賦值運算符的目的是完成資源的復制或拷貝。

【例5-12】通過重載賦值運算符,演示如何實現(xiàn)自定義類的賦值。對于上面出現(xiàn)的s2=s1,編譯器在工作時會檢查每個非靜態(tài)數(shù)據(jù)成員。如果它們都屬內(nèi)部類型(即基本類型或非用戶自定義的類型),則右邊的實例賦給左邊。如果是自定義類型對象,并且該類定義了重載賦值運算符函數(shù),則自動調(diào)用該函數(shù),否則調(diào)用默認拷貝構造函數(shù)進行按成員賦值。5.5.1拷貝的需要性

對象的拷貝分為淺拷貝和深拷貝兩種。假定對象s擁有一個資源,用s的值創(chuàng)建一個t對象。如果t僅僅是在二進制內(nèi)存空間上對s的拷貝,那就意味著t也擁有這個資源了。那么該資源屬于s,還是屬于t,資源歸屬權不清,將引起資源管理混亂。這種只復制成員(如指針指向)不復制資源的情況,稱為淺拷貝,見圖5-1。對于既復制成員又復制資源的情況,稱為深拷貝,見圖5-2。5.5拷貝構造函數(shù)圖5-1淺拷貝圖5-2深拷貝所有類都有一個默認的拷貝函數(shù),它是在編譯時由系統(tǒng)自動提供的函數(shù)。默認拷貝函數(shù)所做的工作是完成淺拷貝。若要實現(xiàn)深拷貝,必須再提供拷貝函數(shù),稱為拷貝構造函數(shù)。

當我們在堆內(nèi)存中分配了資源后,就要提供一個拷貝構造函數(shù);當某類的對象需要使用硬件設備時,就要提供一個拷貝構造函數(shù)。

需要說明的是,對于運算符“=”,我們也能達到“拷貝”的要求。它有兩種方法:一種是按默認方法進行簡單賦值,即淺拷貝;另一種是按用戶要求進行資源拷貝,即深拷貝。深拷貝時必須重載“=”運算符。5.5.2拷貝的發(fā)生

拷貝構造函數(shù)首先是構造函數(shù),是構造函數(shù)就不可人為調(diào)用,而是由系統(tǒng)自動調(diào)用的。在下列三種情況下,將會發(fā)生調(diào)用拷貝構造函數(shù)。

1.用對象構造對象

當用一個對象去構造另一個對象,即用一個對象初始化一個新對象時,將調(diào)用拷貝構造函數(shù)。例如:

Students(“Jenny”); //調(diào)用構造函數(shù)

Studentt=s; //調(diào)用拷貝構造函數(shù)

或者

Students(“Jenny”);

Studentt(s); //用s對象去構造t對象

這兩種寫法的作用是一樣的,但是要注意,若用Students("Jenny");Studentt=s;,則它是賦值而不是初始化,它并不會調(diào)用用戶自定義拷貝構造函數(shù)。它首先檢查有無重載的“=”運算符函數(shù),若無則調(diào)用系統(tǒng)默認的拷貝構造函數(shù),完成淺拷貝。若s不是Student類的對象,則要進行轉換,這將在下一節(jié)中再介紹。

2.對象作為函數(shù)參數(shù)

當對象作為函數(shù)參數(shù)傳遞時,將調(diào)用拷貝構造函數(shù)。例如,在下面的代碼中,形參s是t的一個拷貝。

Studentfun(Students)//普通,調(diào)用它是實參拷貝給形參

{Studenttemp;

returntemp;

//返回對象,發(fā)生拷貝

3.函數(shù)返回一個對象

當一個函數(shù)返回一個對象時,將調(diào)用拷貝構造函數(shù),進行對象的拷貝。

【例5-13】雖然不能用臨時對象來初始化引用,卻可以將臨時對象拷貝給一個同類對象。

#include<iostream>

#include<string>

usingnamespacestd;

classPerson

{protected:運行結果:

Construct Kate //構造wom

Construct Alice

//構造temp

DestructingAlice //析構temp,它是局部對象

DestructingAlice //析構臨時對象

mainend

DestructingAlice //在主函數(shù)結束后析構wom析構temp時,temp返回temp拷貝的臨時對象;在主函數(shù)中完成wom=Set()拷貝后,該臨時對象析構。創(chuàng)建臨時對象時調(diào)用的是拷貝構造函數(shù)。由于本例沒有提供拷貝構造函數(shù),因此進行的是淺拷貝。所以在本例的輸出中發(fā)生了構造與析構不是成對出現(xiàn)的現(xiàn)象。若該對象在堆中分配了空間,再用此方法就會出錯。

【例5-14】演示當對象在堆中申請分配一個資源時,淺拷貝將出錯。5.5.3實現(xiàn)拷貝的方法

淺拷貝將會出現(xiàn)只復制指針不復制指針指向單元內(nèi)的值或對空指針賦值的問題。要想實現(xiàn)深拷貝,有兩種方法:方法一是重載賦值運算符;方法二是自定義拷貝構造函數(shù)。重載賦值運算符前面已作了介紹,在此主要介紹自定義拷貝構造函數(shù)。

定義拷貝構造函數(shù)的一般格式為:

類名::函數(shù)名(類名&對象名)例如:Student::Student(Student&s)拷貝構造函數(shù)名與類同名,參數(shù)表中是本對象的引用。由于參數(shù)的不同,構造函數(shù)也不同。

因為自定義拷貝構造函數(shù)是在用拷貝的方式創(chuàng)建對象,所以必須有一個對象作為實參。又因為它是構造函數(shù),在它執(zhí)行時對象還未創(chuàng)建,還是一片空白,所以它的參數(shù)只能使用引用的方式,如Student(Student&s)。若用拷貝的方式,如Student(Students),就會使任何調(diào)用都陷入無限的遞歸中。5.5.4拷貝構造函數(shù)的使用

對于拷貝構造函數(shù),首先考慮的問題是是否需要。作為一般的經(jīng)驗,若類中有指針作為數(shù)據(jù)成員,就有可能需要拷貝構造函數(shù),或者說當需要析構函數(shù)來釋放資源時,就需要拷貝構造函數(shù)。

【例5-15】演示拷貝構造函數(shù)在對象復制中的應用,將例5-11用拷貝構造函數(shù)的方法實現(xiàn)。運行結果:

Welcometomyworld

Welcometomyworld

通過上例實現(xiàn)了在拷貝數(shù)據(jù)的同時拷貝資源的問題。對于上面出現(xiàn)的Strings2=s1,編譯器在工作時,按聲明的順序檢查每個非靜態(tài)數(shù)據(jù)成員。如果它們是非類類型,則右邊的實例被拷貝到左邊。如果是類類型,并且該類定義了拷貝構造函數(shù),則自動調(diào)用該函數(shù),否則調(diào)用默認拷貝構造函數(shù)進行按成員賦值。默認拷貝構造函數(shù)與默認構造函數(shù)不同。默認構造函數(shù)是空函數(shù),什么也不做,一旦提供了構造函數(shù),原來的默認構造函數(shù)就消失。默認拷貝構造函數(shù)執(zhí)行淺拷貝,當程序員提供了拷貝構造函數(shù)之后,默認拷貝函數(shù)仍存在,并還在發(fā)揮作用。當然對于上面的例題,若不在堆中申請空間,用類庫中的string類實現(xiàn)就很方便。

【例5-16】演示類庫中的string類的使用。

#include<iostream>

#include<string>

usingnamespacestd;

classString5.6.1系統(tǒng)的自動轉換

對于基本的數(shù)據(jù)類型,當兩個不同類型的數(shù)據(jù)相互運算時,系統(tǒng)自動向高類型數(shù)據(jù)轉換。例如:

#include<iostream>

usingnamespacestd;

voidmain()

{cout<<8/5<<'\t'<<8.0/5<<endl;}5.6類?型?的?轉?換運行結果:

1 1.6

其中,1是int,1.6是double。

以上的操作數(shù)是基本類型,由系統(tǒng)自動進行轉換,稱為隱式轉換。也可以進行強制轉換,稱為顯式轉換。例如:

#include<iostream>

usingnamespacestd;

voidmain()

{cout<<(double)8/5<<'\t'<<8.0/5<<endl;}運行結果:

1.6 1.6

由于自定義類型對編譯器來說是不可知的,程序員對類型的定義只是告訴編譯器有了這些新類型,因此若要將基本類型轉換為一個自定義類型,轉換方法必須由用戶告知。告知方法就是提供一個用于轉換的構造函數(shù),也可以編一個轉換函數(shù)。5.6.2轉換構造函數(shù)

通過使用構造函數(shù),既可以將基本類型轉換為用戶自定義類型,也可以將用戶自定義類型轉換為基本類型。當構造函數(shù)的功能不作初始化而用于類型轉換時,我們稱該構造函數(shù)為轉換構造函數(shù)。

實現(xiàn)轉換的方法是:定義含有一個參數(shù)的構造函數(shù),并在函數(shù)體中完成所需要的類型轉換。

【例?5-17】將實型數(shù)據(jù)中以米為單位的值,轉換為以英尺和英寸為單位的值。

#include<iostream>

usingnamespacestd;

classEnglish

{private:

intfeet;floatinches;voidmain()

{Englisheng; //調(diào)用第一個構造函數(shù)

eng=8.0f; //調(diào)用轉換構造函數(shù)

eng.Display();

EnglishengA(8); //調(diào)用第三個構造函數(shù)

engA.Display();

EnglishengB(26,2.88f); //調(diào)用第二個構造函數(shù)

engB.Display();

}運行結果:

feet:26inches:2.88

feet:26inches:2.88

feet:26inches:2.88

本例通過一個用于轉換的構造函數(shù)實現(xiàn)了公制轉換英制。執(zhí)行eng=8.0f;就調(diào)用了轉換構造函數(shù)English(floatmetres),將公制的數(shù)據(jù)8.0轉換為英制的對象eng。因為eng=8.0f是賦值運算,所以編譯器首先檢查是否有重載的賦值運算符,若無,則使用構造函數(shù)進行轉換。若無構造函數(shù)就報錯。比較一下eng=8.0f與Convertereng(26,2.88f)的區(qū)別,不難發(fā)現(xiàn),雖然是同樣的結果,但是使用的條件和概念是不同的。用eng=8的方法是轉換,用Convertereng(26,2.88f)的方法是構造。用于類型轉換的構造函數(shù)有且僅有一個參數(shù),而構造函數(shù)可以有多個參數(shù)。

添加一個實現(xiàn)轉換功能的成員函數(shù)也能實現(xiàn)上述要求。用了轉換構造函數(shù),就可用“=”運算符來達到轉換的目的,既方便,又提高可讀性。5.6.3轉換函數(shù)

除了構造函數(shù)可以實現(xiàn)類型轉換外,轉換函數(shù)也能實現(xiàn)類型轉換。轉換函數(shù)是一種特殊類型的成員函數(shù)。它定義了一個由用戶定義的轉換,以便把一個類對象轉換為基本類型或其他類型。

轉換函數(shù)的原型為:operator目標類型();,它的返回類型就是目標類型。即在類體中通過指定關鍵字operator,并在其后加上轉換后的目標類型,就聲明了轉換函數(shù)。例如,使用Converter::operatorfloat()就能將Converter類的對象轉換為實型數(shù)據(jù)。轉換函數(shù)既可以將當前對象轉換為基本型對象,也可以將當前對象轉換為其他類型對象。例如Converter::operatorOtherclass()。

【例5-18】公英制數(shù)據(jù)的互換。

#include<iostream>

usingnamespacestd;

classConverter

{private:

intfeet;floatinches;intmetre;public:

Converter(){feet=0;inches=0.0;}

Converter(float); //轉換構造函數(shù)

operatorfloat() //轉換函數(shù),將對象轉換為實型

{floaty;y=inches/12;y+=float(feet);return(y/3.28f);}

voidDisplay()

{cout<<feet<<“and”<<inches<<endl;}

//輸出英尺和英寸

};Converter::Converter(floatmetres)

//基本型→自定義類型

{floaty;y=3.28f*metres;

feet=int(y);

inches=12*(y-feet);

}運行結果:

27and10.56

27and10.56

8.5

對象eng的定義調(diào)用了第二個構造函數(shù),在此它將實型數(shù)8.5轉換為類的Converter對象。若用ConverterEng(8.5f);,則也是調(diào)用第二個構造函數(shù),兩者在此是等價的。轉換和構造,主要看函數(shù)的功能。對于operatorfloat(){}函數(shù),原來介紹的是返回類型operator運算符(),現(xiàn)在運算符被類型float取代,此時意為對實型數(shù)據(jù)的運算重載,運算結果和返回類型都應當是實型,所以不需要也不能再指定返回類型。當出現(xiàn)“=”運算右邊是float型、左邊是Converter型的對象時,自動調(diào)用該轉換函數(shù)。

【例5-19】將類對象轉換為字符串,即將自定義類型轉換為基本類型。

#include<iostream>

#include<string>

usingnamespacestd;

constintSIZE=60;

classString

{private:

charstr[SIZE];運行結果:

Enterastring:china↙

Stringinobject=china

Stringp=china

Stringinobject=NewString

運行結果分析:在匹配時,因為p=obj結果為字符指針,它將用戶自定義類型轉換為基本類型,所以匹配operatorchar*();函數(shù)。其中,char*是函數(shù)的返回類型。因為obj=s結果為對象,先查找有無重載的賦值運算,沒有則調(diào)用構造函數(shù),所以匹配String(char*s);函數(shù)。

對于將基本類型轉換為對象,一般用構造函數(shù)作為轉換函數(shù)來實現(xiàn);對于將對象轉換為基本類型,一般通過指定關鍵字operator,并在其后加上轉換的目標類型來聲明轉換函數(shù)。5.6.4轉換函數(shù)的匹配方式

上面已經(jīng)介紹了用轉換函數(shù)和用構造函數(shù)實現(xiàn)類型轉換。從結果來看,它們的共同特點是,編譯器都能自動地調(diào)用最佳匹配的函數(shù)。這種自動調(diào)用方式正是轉換函數(shù)的一個奇妙特征之一,它給我們帶來了方便。如在例5-19中的p=obj和obj=s,編譯器分別自動匹配operatorchar*()和String(char*s)。那么在既有轉換又有賦值時,編譯器將如何匹配呢?

匹配的依據(jù),根據(jù)操作性質和參數(shù)的類型而定。若給出一種操作(轉換),找不到與之匹配的函數(shù),就出錯。

【例5-20】演示各個函數(shù)的匹配方式與次序。

#include<iostream>

usingnamespacestd;

classTest

{public:

Test(){cout<<"Thefirstfunction"<<endl;a=0;b=0;}Test(floatx){cout<<"Thesecondfunction"<<endl;a=x;b=8.8f;}

voidoperator=(Test&s){cout<<"Thethirdfunction"<<endl;a=s.a;b=s.b;}

Test(Test&p){cout<<"Thefourthfunction"<<endl;a=p.a;b=p.b;}

operatorfloat(){cout<<"Thefifthfunction"<<endl;returna+b;}

operatorint(){cout<<"Thesixthfunction"<<endl;;returnint(a)+int(b);}

voidDisplay(){cout<<a<<'\t'<<b<<endl;}d1=d2; //它是賦值運算,調(diào)用第三個函數(shù)

cout<<“d1:”;d1.Display();

Testd3(d2);

//調(diào)用第四個構造函數(shù)

cout<<"d3:";d3.Display();

m=d2; //調(diào)用第五個函數(shù)

cout<<"m:"<<m<<endl;

n=d2; //調(diào)用第六個函數(shù)

cout<<"n:"<<n<<endl;

}運行結果:

Thefirstfunction

d1: 00

Thesecondfunction

d2:1.55 8.8

Thethirdfunction

d1:1.55 8.8

Thefourthfunction

d3:1.55 8.8

Thefifthfunction

m:10.35

Thesixthfunction

n:95.6.5轉換函數(shù)的作用

通過前面的幾個例子不難得出這樣的結論,要想實現(xiàn)不同類型的轉換,可以用轉換構造函數(shù),也可以用轉換函數(shù)。那么為什么要引入轉換函數(shù)呢?以下說明原因。若要對兩個不同類的對象進行多種操作,或對象與內(nèi)部類型數(shù)據(jù)進行多種操作,比如我們要使String的對象與int數(shù)進行“+”操作,就用:

classString

{friendoperator+(String&,int);

friendoperator+(int,String&);

public:

String(inta){value=a;}

};

String對象與整型參數(shù)就需要兩個重載函數(shù),若還要做“-”操作,則需要的重載函數(shù)將翻倍,更不用說還有“*”、“/”、位運算、邏輯和關系運算等,若再增加參數(shù)個數(shù),就又有多種組合。因此C++中提供了轉換函數(shù),通過它,每個類都可以定義一組“可被應用在該類型對象上的轉換”,從而將對象轉換成我們所需要的內(nèi)部類型。

【例5-21】演示轉換函數(shù)的作用。

#include<iostream>

usingnamespacestd;

classTest

{private:

intvalue; doublescore;

public:

Test(inta,doublex)

{value=a;score=x;}

operatordouble(){returnscore;}

//只用一個函數(shù)就可完成8種運算

};voidmain()

{Testa(34,7.8);intx;doubley;

x=a;y=a; //先用轉換函數(shù),轉換為基本類型

cout<<x+3.14<<endl;

//然后就可以進行基本類型數(shù)據(jù)運算

cout<<3.14+x<<endl;

cout<<3.14-y<<endl;

cout<<3.14*y<<endl;

cout<<3.14/x<<endl;

cout<<5+x<<endl;

}從上面的程序可見,有了轉換函數(shù),我們就可以先將自定義類型轉換為基本類型,以后所做的操作實際上就是兩個基本類型數(shù)據(jù)的操作。例如,當進行x=a或y=a時,編譯器隱式地調(diào)用轉換函數(shù)Test::operatordouble(),將對象a轉換為double類型,于是就可以做常規(guī)的運算了。其實質就是先轉換后操作,從而解決復雜的重載運算符操作問題?!?如果運算符重載比用明確的函數(shù)調(diào)用使程序更清晰,那么就使用運算符重載。運算符重載的目的是使用戶自定義類型也能使用運算符操作,從而提高程序的可讀性。

●?運算符的重載是通過編寫函數(shù)定義來實現(xiàn)的。函數(shù)名必須由關鍵字operator和其后要重載的運算符組成。

●?在運算符重載時不要改變運算符原來的優(yōu)先級、結合性和操作個數(shù)等性質。

●?運算符重載提高了C++的可擴展性。本章要點●?運算符的重載函數(shù)可以是類的成員函數(shù),也可以是類的友元。對于運算符=、()、[]和->的重載函數(shù),可作為類的成員函數(shù),但不可作為友元函數(shù)。

●?若沒有定義拷貝構造函數(shù),那么在編譯時系統(tǒng)將自動提供一個默認的拷貝構造函數(shù),完成淺拷貝。

●?默認拷貝構造函數(shù)與默認構造函數(shù)不同,默認構造函數(shù)是空函數(shù)。

●?若在堆內(nèi)存中分配了資源,就要提供一個拷貝構造函數(shù),實現(xiàn)深拷貝。●?拷貝構造函數(shù)由系統(tǒng)自動調(diào)用,在三種情況下會發(fā)生調(diào)用拷貝構造函數(shù)。

●?轉換函數(shù)通過定義的函數(shù)來完成不同類型對象之間的轉換。

●?用于類型轉換的構造函數(shù)最多只能含有一個參數(shù)。

●?轉換函數(shù)并不一定要由重載構造函數(shù)或用operator關鍵字來完成,一般函數(shù)也能實現(xiàn)轉換。兩者的區(qū)別是:用構造函數(shù)可以在出現(xiàn)“=”運算符時由系統(tǒng)自動調(diào)用;用一般函數(shù)則需要顯式調(diào)用。

●?選用何種方式轉換,要由語義是否清晰來決定。一、概念

1.運算符重載本質上是通過

來實現(xiàn)操作要求的。

2.運算符重載的目的是:

。

3.雙目運算符的重載函數(shù)作為類的友元函數(shù)時,對函數(shù)中參數(shù)的次序是

要求的。

4.雙目運算符的重載函數(shù)作為類的成員函數(shù)時,它有

參數(shù),此時可以解釋為

,自動調(diào)用該函數(shù)。練習

5.在重載前后增(減)量操作符時,C++中規(guī)定,前增(減)量返回

,后增(減)量返回

。

6.在

的情況下程序員要提供拷貝構造函數(shù)。

7.拷貝構造函數(shù)參數(shù)的特點是:

。轉換函數(shù)的特點是:

。二、分析

1.指出下面程序的運行結果。

#include<iostream>

usingnamespacestd;

classRMB

{public:

RMB(){yuan=0;}

RMB(intd){yuan=d;} RMB&operator++();

RMBoperator++(int);

voidDisplay()

{cout<<yuan<<endl;}

protected:

intyuan;

};

RMB&RMB::operator++()

{++yuan;

return*this;

}RMBRMB::operator++(int)

{RMBtemp(*this);

yuan++;returntemp;

}

voidmain()

{RMBd1(50),d2(50);

(++d1).Display();

(d2++).Display();

++++++d1;

(d2++);

d1.Display();

d2.Display();

}2.指出下面程序的運行結果。

#include<iostream>

usingnamespacestd;

classIncrease

{public:

Increase(intx,floaty)

{value=x;val=y;}

Increase&operator++();

Increaseoperator++(int);

voidDisplay()

{cout<<"thevalueis"<<value;cout<<"thevalis"<<val<<endl;}protected:

intvalue;floatval;

};

Increase&Increase::operator++()

{value++;val++;

return*this;

}

IncreaseIncrease::operator++(int)

{Increasetemp(*this);

value++;val++;

returntemp;

}voidmain()

{Increasen(20,3.1f);

n.Display();

(n++).Display();

n.Display();

++n;

n.Display();

++(++n);

n.Display();

n++;

n.Display();

}3.指出下面程序的運行結果。

#include<iostream>

usingnamespacestd;

classPerson

{public:

Person(char*pN="noname");

Person(Person&p);

~Person();

private:

char*pName;

};Person::Person(char*pN)

{cout<<"ConstructingPerson"<<pN<<endl;

pName=newchar[strlen(pN)+1];

if(pName!=0)

{strcpy(pName,pN);}

}

Person::Person(Person&p)

{cout<<"Copting"<<p.pName<<"intoitsownblock\n";

pName=newchar[strlen(p.pName)+1];

if(pName!=0)strcpy(pName,p.pName);

}Person::~Person()

{cout<<"Destructing"<<pName<<endl;deletepName;}

Personfun(Persona)

{Personms(a);

returnms;

}

voidmain()

{Personp("Amadis");

Persons=fun(p);

Personms("Kansas");

Personns=ms;

}

4.改正下面程序中的錯誤。要求不用string類庫,實現(xiàn)兩個字符串的連接。

#include<iostream>

usingnamespacestd;

classString

{private:

charstr[80];

public:

String(){strcpy(str,“”);}

String(char*p)

{strcpy(str,p);}voidoperator+=(Strings)

{if(strlen(str)+strlen(s.str)<80)

{strcat(str,s.str);return(*this); }

else

{cout<<"stringistoolong";

return(*this);

}

}

voidDisplay(){cout<<str<<endl;}

};voidmain()

{Strings1="IuseC++"; Strings2="testtheconnect";

s1+=s2;

s1.Display();

Strings3;

s3=s2;

s3.Display();

}

5.下面的程序若第一次輸入:95,第二次輸入:59,請分析兩次運行的結果。

#include<iostream>

usingnamespacestd;

classArray

{protected:

intsize;int*p;

public:

Array(int=3);

~Array(){delete[]p;}

int&operator[](int);

};Array::Array(intn)

{size=n;p=newint[size];for(inti=0;i<size;i++)p[i]=0;}

int&Array::operator[](intx)

{if

溫馨提示

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

評論

0/150

提交評論