版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)
文檔簡介
面向?qū)ο蟪绦蛟O(shè)計的特征抽象性(Abstraction)封裝性(Encapsulation)繼承性(Inheritance)多態(tài)性(Polymorphism)(一)多態(tài)性的概念polymorphism,“manyforms”:即多種形態(tài)在自然語言中,多態(tài)性即是“一詞多義”;更準(zhǔn)確地說,多態(tài)性是指相同的動詞作用到不同類型的對象上,例如: 駕駛摩托車
駕駛汽車
駕駛飛機(jī)
駕駛輪船駕駛宇宙飛船什么是多態(tài)性?(OOP)當(dāng)不同對象接受到相同的消息產(chǎn)生不同的動作,這種性質(zhì)稱為多態(tài)性。通俗地說,多態(tài)性是指用一個名字定義不同的函數(shù),這些函數(shù)執(zhí)行不同但又類似的操作,即用同樣的接口訪問功能不同的函數(shù),從而實現(xiàn)“一個接口,多種方法”。在面向?qū)ο蠓椒ㄖ幸话闶沁@樣表述多態(tài)性的:向不同的對象發(fā)送同一個消息,不同的對象在接收時會產(chǎn)生不同的行為(即方法)。也就是說,每個對象可以用自己的方式去響應(yīng)共同的消息。多態(tài)性的例子在C語言中,由于不支持多態(tài),求絕對值的動作要求三個不同的函數(shù)名字:abs(),labs(),fabs()分別用來求整數(shù),長整數(shù)、浮點數(shù)的絕對值。在C++語言中,由于支持多態(tài),求絕對值的動作可以只用一個函數(shù)名:abs()面向?qū)ο蟪绦蛟O(shè)計的精華通過一個簡單的接口對不同的實現(xiàn)進(jìn)行概念上的簡化。classwalkman{ virtualvoidon()=0; virtualvoidoff()=0; virtualvoidplay()=0; virtualvoidrecord()=0; virtualvoidstop()=0;};Cassetteplayer,CDplayer,MDplayer,MP3player;提供給用戶相同的接口應(yīng)用多態(tài)性的好處多態(tài)性(polymorphism)是面向?qū)ο蟪绦蛟O(shè)計的一個重要特征。利用多態(tài)性可以設(shè)計和實現(xiàn)一個易于擴(kuò)展的系統(tǒng)。多態(tài)應(yīng)用于OOP的目的是允許用一個名字來指定動作的一般類(即邏輯上相似的動作)。從而帶來以下的好處:提高了處理問題的抽象級別;降低了程序設(shè)計時的復(fù)雜性;(程序員只需記住一個接口,而不是好幾個。)從系統(tǒng)實現(xiàn)的角度看,多態(tài)性分為兩類:靜態(tài)多態(tài)性和動態(tài)多態(tài)性。以前學(xué)過的函數(shù)重載、運算符重載和模板實現(xiàn)的多態(tài)性屬于靜態(tài)多態(tài)性,在程序編譯時系統(tǒng)就能決定調(diào)用的是哪個函數(shù),因此靜態(tài)多態(tài)性又稱編譯時的多態(tài)性。靜態(tài)多態(tài)性是通過函數(shù)的重載實現(xiàn)的。動態(tài)多態(tài)性是在程序運行過程中才動態(tài)地確定操作所針對的對象。它又稱運行時的多態(tài)性。動態(tài)多態(tài)性是通過虛函數(shù)(virtualfunction)實現(xiàn)的。C++實現(xiàn)的多態(tài)性編譯時多態(tài)性:函數(shù)重載運算符重載模板
運行時多態(tài)性:借助虛函數(shù)來獲得在C++中,多態(tài)性的實現(xiàn)與聯(lián)編(Binding)這一概念有關(guān)。(二)
靜態(tài)聯(lián)編和動態(tài)聯(lián)編什么叫聯(lián)編(Binding)?一個源程序需要經(jīng)過編譯、連接,才能成為可執(zhí)行代碼。上述過程中需要將一個函數(shù)調(diào)用鏈接上相應(yīng)的函數(shù)代碼,這一過程稱為聯(lián)編。聯(lián)編的目的是要建立函數(shù)調(diào)用與函數(shù)體之間的聯(lián)系,即將一個函數(shù)調(diào)用連接到一函數(shù)的入口地址。C++支持兩種聯(lián)編靜態(tài)聯(lián)編:在程序被編譯時進(jìn)行聯(lián)編;(早聯(lián)編)程序執(zhí)行快,但靈活性較小。動態(tài)聯(lián)編:在程序運行時聯(lián)編。(晚聯(lián)編,滯后聯(lián)編)靈活性高,程序執(zhí)行慢。動態(tài)聯(lián)編是C++實現(xiàn)運行時多態(tài)性的關(guān)鍵因素。一個典型的例子例7.1先建立一個Point(點)類,包含數(shù)據(jù)成員x,y(坐標(biāo)點)。以它為基類,派生出一個Circle(圓)類,增加數(shù)據(jù)成員r(半徑),再以Circle類為直接基類,派生出一個Cylinder(圓柱體)類,再增加數(shù)據(jù)成員h(高)。要求編寫程序,重載運算符“<<”和“>>”,使之能用于輸出以上類對象。對于一個比較大的程序,應(yīng)當(dāng)分成若干步驟進(jìn)行。先聲明基類,再聲明派生類,逐級進(jìn)行,分步調(diào)試。(1)聲明基類Point類源碼參見7-1/1.cpp運行結(jié)果為x=3.5,y=6.4p(new):[8.5,6.8](2)聲明派生類Circle源碼參見7-1/2.cpp運行結(jié)果為originalcircle:(輸出原來的圓的數(shù)據(jù))x=3.5,y=6.4,r=5.2,area=84.9486newcircle:(輸出修改后的圓的數(shù)據(jù))Center=[5,5],r=7.5,area=176.714pRef:[5,5](輸出圓的圓心“點”的數(shù)據(jù))(3)聲明Circle的派生類Cylinder源碼參見7-1/3.cpp運行結(jié)果如下:originalcylinder:(輸出cy1的初始值)x=3.5,y=6.4,r=5.2,h=10(圓心坐標(biāo)x,y。半徑r,高h(yuǎn))area=496.623,volume=849.486(圓柱表面積area和體積volume)newcylinder:(輸出cy1的新值)Center=[5,5],r=7.5,h=15(以[5,5]形式輸出圓心坐標(biāo))area=1060.29,volume=2650.72(圓柱表面積area和體積volume)pRefasaPoint:[5,5](pRef作為一個“點”輸出)cRefasaCircle:Center=[5,5],r=7.5,area=176.714(cRef作為一個“圓”輸出)在本例中存在靜態(tài)多態(tài)性,這是運算符重載引起的??梢钥吹剑诰幾g時編譯系統(tǒng)即可以判定應(yīng)調(diào)用哪個重載運算符函數(shù)。下面再看一個例子。就是昨天的6-10.cpp通過本例可以看到:用指向基類對象的指針變量指向子類對象是合法的、安全的,不會出現(xiàn)編譯上的錯誤。但在應(yīng)用上卻不能完全滿足人們的希望,通過指向基類對象的指針,只能訪問派生類中的基類成員,而不能訪問派生類增加的成員。引入派生類后的對象指針在第2章中我們介紹過一般對象的指針,它們彼此獨立,不能混用。引入派生類后,由于派生類是由基類派生出來的,因此指向基類和派生類的指針也是相關(guān)的。例7-0.cpp程序運行結(jié)果如下:my_base--------5050my_base--------1020從程序運行的結(jié)果可以看出,雖然執(zhí)行語句mp=&mc;后,指針mp已經(jīng)指向了對象mc,但是它所調(diào)用的函數(shù)(mp->show()),仍然是其基類對象的show(),顯然這不是我們所期望的。出現(xiàn)這個問題的原因以及解決方法,我們將在下一節(jié)介紹。在此先說明引入派生類后,使用對象指針應(yīng)注意的幾個問題:聲明為指向基類對象的指針可以指向它的公有派生的對象,但不允許指向它的私有派生的對象,例如:classbase{//…};classderive:privatebase{//…};voidmain(){baseop1,*ptr;//定義基類base的對象op1及base類指針ptrderiveop2;//定義派生類derive的對象op2ptr=&op1;//使指針ptr指向?qū)ο髈p1ptr=&op2;//錯誤,不允許將base類指針ptr指向它的私有派生類對象op2…}允許將一個聲明為指向基類的指針指向公有派生類的對象,但是不能將一個聲明為指向派生類對象的指針指向其基類的對象。聲明為指向基類的指針,當(dāng)其指向公有派生的對象時,只能用它來直接訪問派生類中從基類繼承來的成員,而不能直接訪問公有派生類中定義的成員,例如:
classA{//...public:voidprint1();};classB:publicA{//...public:print2();};voidmain(){Aop1,*ptr;//定義基類A的對象op1和基類指針ptrBop2;//定義派生類B的對象op2ptr=&op1;//使指針ptr指向基類對象op1ptr->print1();//調(diào)用基類函數(shù)print1()ptr=&op2;//使指針ptr指向派生類對象op2ptr->print1();//調(diào)用對象op2從其基類繼承來的成員函數(shù)print1()ptr->print2();//錯誤,基類指針ptr不能訪問派生類中定義的成員函數(shù)print2()}若想訪問其公有派生類的特定成員,可以將基類指針用顯式類型轉(zhuǎn)換為派生類指針。例如那條錯誤的語句可改寫成:((B*)ptr)->print2();外層的括號表示對ptr強(qiáng)制轉(zhuǎn)換,而不是返回類型。在上例中,雖然基類指針mp已經(jīng)指向了派生類對象mc,但是它所調(diào)用的函數(shù)(mp->show())仍然是其基類對象的show()。這說明,不管指針mp當(dāng)前指向哪個對象(基類對象或派生類對象),mp->show()調(diào)用的都是基類中定義的show()函數(shù)版本。其原因在于普通成員函數(shù)調(diào)用是在編譯時靜態(tài)聯(lián)編的。在這種情況下,若要調(diào)用派生類中的成員函數(shù),必須采用顯式的方法,例如:mc.show();或者采用對指針強(qiáng)制類型轉(zhuǎn)換的方法,如:((my_class*)mp)->show();在上例中,使用對象指針的目的是為了表達(dá)一種動態(tài)的性質(zhì),即當(dāng)指針指向不同對象時執(zhí)行不同的操作,但是采用以上兩種方法都沒有起到這種作用。要研究的問題是:當(dāng)一個基類被繼承為不同的派生類時,各派生類可以使用與基類成員相同的成員名,如果在運行時用同一個成員名調(diào)用類對象的成員,會調(diào)用哪個對象的成員?也就是說,通過繼承而產(chǎn)生了相關(guān)的不同的派生類,與基類成員同名的成員在不同的派生類中有不同的含義。也可以說,多態(tài)性是“一個接口,多種方法”。(三)虛函數(shù)3.1虛函數(shù)的作用3.2靜態(tài)關(guān)聯(lián)與動態(tài)關(guān)聯(lián)3.3在什么情況下應(yīng)當(dāng)聲明虛函數(shù)3.4虛析構(gòu)函數(shù)3.1虛函數(shù)的作用在類的繼承層次結(jié)構(gòu)中,在不同的層次中可以出現(xiàn)名字相同、參數(shù)個數(shù)和類型都相同而功能不同的函數(shù)。編譯系統(tǒng)按照同名覆蓋的原則決定調(diào)用的對象。在例7.1程序中用cy1.area()調(diào)用的是派生類Cylinder中的成員函數(shù)area。如果想調(diào)用cy1中的直接基類Circle的area函數(shù),應(yīng)當(dāng)表示為:cy1.Circle::area()。用這種方法來區(qū)分兩個同名的函數(shù)。但是這樣做很不方便。人們提出這樣的設(shè)想,能否用同一個調(diào)用形式,既能調(diào)用派生類又能調(diào)用基類的同名函數(shù)。在程序中不是通過不同的對象名去調(diào)用不同派生層次中的同名函數(shù),而是通過指針調(diào)用它們。例如,用同一個語句“pt->display();”可以調(diào)用不同派生層次中的display函數(shù),只需在調(diào)用前給指針變量pt賦以不同的值(使之指向不同的類對象)即可。C++中的虛函數(shù)就是用來解決這個問題的。虛函數(shù)的作用是允許在派生類中重新定義與基類同名的函數(shù),并且可以通過基類指針或引用來訪問基類和派生類中的同名函數(shù)。例7.2基類與派生類中有同名函數(shù)。未使用虛函數(shù)的情況,參見源碼7-2/1.cpp運行結(jié)果如下:num:1001 (stud1的數(shù)據(jù))name:Liscore:87.5num:2001 (grad1中基類部分的數(shù)據(jù))name:wangscore:98.5下面對程序作一點修改,在Student類中聲明display函數(shù)時,在最左面加一個關(guān)鍵字virtual,即:virtualvoiddisplay();這樣就把Student類的display函數(shù)聲明為虛函數(shù)。程序其他部分都不改動。源碼參見7-2/2.cpp再編譯和運行程序,請注意分析運行結(jié)果:num:1001(stud1的數(shù)據(jù))name:Liscore:87.5num:2001(grad1中基類部分的數(shù)據(jù))name:wangscore:98.5pay=1200(這一項以前是沒有的)由虛函數(shù)實現(xiàn)的動態(tài)多態(tài)性就是:同一類族中不同類的對象,對同一函數(shù)調(diào)用作出不同的響應(yīng)。虛函數(shù)的使用方法是:在基類用virtual聲明成員函數(shù)為虛函數(shù)。這樣就可以在派生類中重新定義此函數(shù),為它賦予新的功能,并能方便地被調(diào)用。在類外定義虛函數(shù)時,不必再加virtual。在派生類中重新定義此函數(shù),要求函數(shù)名、函數(shù)類型、函數(shù)參數(shù)個數(shù)和類型全部與基類的虛函數(shù)相同,并根據(jù)派生類的需要重新定義函數(shù)體。C++規(guī)定,當(dāng)一個成員函數(shù)被聲明為虛函數(shù)后,其派生類中的同名函數(shù)都自動成為虛函數(shù)。因此在派生類重新聲明該虛函數(shù)時,可以加virtual,也可以不加,但習(xí)慣上一般在每一層聲明該函數(shù)時都加virtual,使程序更加清晰。如果在派生類中沒有對基類的虛函數(shù)重新定義,則派生類簡單地繼承其直接基類的虛函數(shù)。定義一個指向基類對象的指針變量,并使它指向同一類族中需要調(diào)用該函數(shù)的對象。通過該指針變量調(diào)用此虛函數(shù),此時調(diào)用的就是指針變量指向的對象的同名函數(shù)。通過虛函數(shù)與指向基類對象的指針變量的配合使用,就能方便地調(diào)用同一類族中不同類的同名函數(shù),只要先用基類指針指向即可。如果指針不斷地指向同一類族中不同類的對象,就能不斷地調(diào)用這些對象中的同名函數(shù)。需要說明:有時在基類中定義的非虛函數(shù)會在派生類中被重新定義(如例7.1中的area函數(shù)),如果用基類指針調(diào)用該成員函數(shù),則系統(tǒng)會調(diào)用對象中基類部分的成員函數(shù);如果用派生類指針調(diào)用該成員函數(shù),則系統(tǒng)會調(diào)用派生類對象中的成員函數(shù),這并不是多態(tài)性行為(使用的是不同類型的指針),沒有用到虛函數(shù)的功能。以前介紹的函數(shù)重載處理的是同一層次上的同名函數(shù)問題,而虛函數(shù)處理的是不同派生層次上的同名函數(shù)問題,前者是橫向重載,后者可以理解為縱向重載。但與重載不同的是:同一類族的虛函數(shù)的首部是相同的,而函數(shù)重載時函數(shù)的首部是不同的(參數(shù)個數(shù)或類型不同)。3.2靜態(tài)關(guān)聯(lián)與動態(tài)關(guān)聯(lián)編譯系統(tǒng)要根據(jù)已有的信息,對同名函數(shù)的調(diào)用作出判斷。對于調(diào)用同一類族中的虛函數(shù),應(yīng)當(dāng)在調(diào)用時用一定的方式告訴編譯系統(tǒng),你要調(diào)用的是哪個類對象中的函數(shù)。這樣編譯系統(tǒng)在對程序進(jìn)行編譯時,即能確定調(diào)用的是哪個類對象中的函數(shù)。確定調(diào)用的具體對象的過程稱為關(guān)聯(lián)(binding)。在這里是指把一個函數(shù)名與一個類對象捆綁在一起,建立關(guān)聯(lián)。一般地說,關(guān)聯(lián)指把一個標(biāo)識符和一個存儲地址聯(lián)系起來。前面所提到的函數(shù)重載和通過對象名調(diào)用的虛函數(shù),在編譯時即可確定其調(diào)用的虛函數(shù)屬于哪一個類,其過程稱為靜態(tài)關(guān)聯(lián)(staticbinding),由于是在運行前進(jìn)行關(guān)聯(lián)的,故又稱為早期關(guān)聯(lián)(earlybinding)。函數(shù)重載屬靜態(tài)關(guān)聯(lián)。在上一小節(jié)程序中看到了怎樣使用虛函數(shù),在調(diào)用虛函數(shù)時并沒有指定對象名,那么系統(tǒng)是怎樣確定關(guān)聯(lián)的呢?是通過基類指針與虛函數(shù)的結(jié)合來實現(xiàn)多態(tài)性的。先定義了一個指向基類的指針變量,并使它指向相應(yīng)的類對象,然后通過這個基類指針去調(diào)用虛函數(shù)(例如“pt->display()”)。顯然,對這樣的調(diào)用方式,編譯系統(tǒng)在編譯該行時是無法確定調(diào)用哪一個類對象的虛函數(shù)的。因為編譯只作靜態(tài)的語法檢查,光從語句形式是無法確定調(diào)用對象的。在這樣的情況下,編譯系統(tǒng)把它放到運行階段處理,在運行階段確定關(guān)聯(lián)關(guān)系。在運行階段,基類指針變量先指向了某一個類對象,然后通過此指針變量調(diào)用該對象中的函數(shù)。此時調(diào)用哪一個對象的函數(shù)無疑是確定的。例如,先使pt指向grad1,再執(zhí)行“pt->display()”,當(dāng)然是調(diào)用grad1中的display函數(shù)。由于是在運行階段把虛函數(shù)和類對象“綁定”在一起的,因此,此過程稱為動態(tài)關(guān)聯(lián)(dynamicbinding)。這種多態(tài)性是動態(tài)的多態(tài)性,即運行階段的多態(tài)性。在運行階段,指針可以先后指向不同的類對象,從而調(diào)用同一類族中不同類的虛函數(shù)。由于動態(tài)關(guān)聯(lián)是在編譯以后的運行階段進(jìn)行的,因此也稱為滯后關(guān)聯(lián)(latebinding)。3.3在什么情況下應(yīng)當(dāng)聲明虛函數(shù)使用虛函數(shù)時,有兩點要注意:只能用virtual聲明類的成員函數(shù),使它成為虛函數(shù),而不能將類外的普通函數(shù)聲明為虛函數(shù)。因為虛函數(shù)的作用是允許在派生類中對基類的虛函數(shù)重新定義。顯然,它只能用于類的繼承層次結(jié)構(gòu)中。一個成員函數(shù)被聲明為虛函數(shù)后,在同一類族中的類就不能再定義一個非virtual的但與該虛函數(shù)具有相同的參數(shù)(包括個數(shù)和類型)和函數(shù)返回值類型的同名函數(shù)。根據(jù)什么考慮是否把一個成員函數(shù)聲明為虛函數(shù)呢?主要考慮以下幾點:首先看成員函數(shù)所在的類是否會作為基類。然后看成員函數(shù)在類的繼承后有無可能被更改功能,如果希望更改其功能的,一般應(yīng)該將它聲明為虛函數(shù)。如果成員函數(shù)在類被繼承后功能不需修改,或派生類用不到該函數(shù),則不要把它聲明為虛函數(shù)。不要僅僅考慮到要作為基類而把類中的所有成員函數(shù)都聲明為虛函數(shù)。應(yīng)考慮對成員函數(shù)的調(diào)用是通過對象名還是通過基類指針或引用去訪問,如果是通過基類指針或引用去訪問的,則應(yīng)當(dāng)聲明為虛函數(shù)。有時,在定義虛函數(shù)時,并不定義其函數(shù)體,即函數(shù)體是空的。它的作用只是定義了一個虛函數(shù)名,具體功能留給派生類去添加。在7.4節(jié)中將詳細(xì)討論此問題。需要說明的是:使用虛函數(shù),系統(tǒng)要有一定的空間開銷。當(dāng)一個類帶有虛函數(shù)時,編譯系統(tǒng)會為該類構(gòu)造一個虛函數(shù)表(virtualfunctiontable,簡稱vtable),它是一個指針數(shù)組,存放每個虛函數(shù)的入口地址。系統(tǒng)在進(jìn)行動態(tài)關(guān)聯(lián)時的時間開銷是很少的,因此,多態(tài)性是高效的。3.4虛析構(gòu)函數(shù)析構(gòu)函數(shù)的作用是在對象撤銷之前做必要的“清理現(xiàn)場”的工作。當(dāng)派生類的對象從內(nèi)存中撤銷時一般先調(diào)用派生類的析構(gòu)函數(shù),然后再調(diào)用基類的析構(gòu)函數(shù)。但是,如果用new運算符建立了臨時對象,若基類中有析構(gòu)函數(shù),并且定義了一個指向該基類的指針變量。在程序用帶指針參數(shù)的delete運算符撤銷對象時,會發(fā)生一個情況:系統(tǒng)會只執(zhí)行基類的析構(gòu)函數(shù),而不執(zhí)行派生類的析構(gòu)函數(shù)。例7.3基類中有非虛析構(gòu)函數(shù)時的執(zhí)行情況。源碼參見7-3.cpp這只是一個示意的程序。p是指向基類的指針變量,指向new開辟的動態(tài)存儲空間,希望用detele釋放p所指向的空間。但運行結(jié)果為:executingPointdestructor表示只執(zhí)行了基類Point的析構(gòu)函數(shù),而沒有執(zhí)行派生類Circle的析構(gòu)函數(shù)。原因是以前介紹過的。如果希望能執(zhí)行派生類Circle的析構(gòu)函數(shù),可以將基類的析構(gòu)函數(shù)聲明為虛析構(gòu)函數(shù),如:virtual~Point(){cout<<″executingPointdestructor″<<endl;}程序其他部分不改動,再運行程序,結(jié)果為executingCircledestructorexecutingPointdestructor先調(diào)用了派生類的析構(gòu)函數(shù),再調(diào)用了基類的析構(gòu)函數(shù),符合人們的愿望。當(dāng)基類的析構(gòu)函數(shù)為虛函數(shù)時,無論指針指的是同一類族中的哪一個類對象,系統(tǒng)會采用動態(tài)關(guān)聯(lián),調(diào)用相應(yīng)的析構(gòu)函數(shù),對該對象進(jìn)行清理工作。如果將基類的析構(gòu)函數(shù)聲明為虛函數(shù)時,由該基類所派生的所有派生類的析構(gòu)函數(shù)也都自動成為虛函數(shù),即使派生類的析構(gòu)函數(shù)與基類的析構(gòu)函數(shù)名字不相同。在設(shè)計類時,最好把基類的析構(gòu)函數(shù)聲明為虛函數(shù)。這將使所有派生類的析構(gòu)函數(shù)自動成為虛函數(shù)。這樣,如果程序中顯式地用了delete運算符準(zhǔn)備刪除一個對象,而delete運算符的操作對象用了指向派生類對象的基類指針,則系統(tǒng)會調(diào)用相應(yīng)類的析構(gòu)函數(shù)。虛析構(gòu)函數(shù)的概念和用法很簡單,但它在面向?qū)ο蟪绦蛟O(shè)計中卻是很重要的技巧。專業(yè)人員一般都習(xí)慣聲明虛析構(gòu)函數(shù),即使基類并不需要析構(gòu)函數(shù),也顯式地定義一個函數(shù)體為空的虛析構(gòu)函數(shù),以保證在撤銷動態(tài)分配空間時能得到正確的處理。構(gòu)造函數(shù)不能聲明為虛函數(shù)。這是因為在執(zhí)行構(gòu)造函數(shù)時類對象還未完成建立過程,當(dāng)然談不上函數(shù)與類對象的綁定。虛函數(shù)是動態(tài)聯(lián)編的基礎(chǔ)virtual關(guān)鍵字的作用:指示C++編譯器對該函數(shù)的調(diào)用進(jìn)行動態(tài)聯(lián)編。盡管可以用對象名和點算符的方式調(diào)用虛函數(shù),即向?qū)ο蟀l(fā)送消息:tri.area()或者rect.area()這時是靜態(tài)聯(lián)編方式。只有當(dāng)訪問虛函數(shù)是通過基類指針s時才可獲得運行時的多態(tài)性。虛函數(shù)與重載函數(shù)的比較1.從形式上說,重載函數(shù)要求函數(shù)有相同的函數(shù)名稱,并有不同的參數(shù)序列;2.重載函數(shù)可以是成員函數(shù)和非成員函數(shù);而虛函數(shù)要求這三項(函數(shù)名稱、返回值和參數(shù)序列)完全相同,即具有完全相同的函數(shù)原型。而虛函數(shù)只能是成員函數(shù)。虛函數(shù)小結(jié)
如果某類中的一個成員函數(shù)被說明為虛函數(shù),這就意味著該成員函數(shù)在派生類中可能有不同的實現(xiàn)。為了實現(xiàn)運行時的多態(tài)性,調(diào)用虛函數(shù)應(yīng)該通過第一次定義該虛函數(shù)的基類對象指針只有通過指針或引用標(biāo)識對象來操作虛函數(shù)時,才對虛函數(shù)采取動態(tài)聯(lián)編方式。如果采用一般類型的標(biāo)識對象來操作虛函數(shù),則將采用靜態(tài)聯(lián)編方式調(diào)用虛函數(shù)。虛函數(shù)與重載函數(shù)的比較(續(xù))3.對重載函數(shù)的調(diào)用是以所傳遞參數(shù)序列的差別(參數(shù)個數(shù)或類型的不同)作為調(diào)用不同函數(shù)的依據(jù);虛擬函數(shù)是根據(jù)對象的不同去調(diào)用不同類的虛擬函數(shù)。虛函數(shù)在運行時表現(xiàn)出多態(tài)功能,正是C++的精髓之一;而重載函數(shù)不具備這一功能。虛函數(shù)小結(jié)(續(xù))構(gòu)造函數(shù)不能是虛擬的,但析構(gòu)函數(shù)可以是虛擬的。
設(shè)計C++程序時,一般在基類中定義處理某一問題的接口和數(shù)據(jù)元素,而在派生類中定義具體的處理方法。通常都將基類中處理問題的接口設(shè)計成虛函數(shù),然后利用基類對象指針調(diào)用虛函數(shù),從而達(dá)到單一接口,多種功能的目的。(四)純虛函數(shù)與抽象類4.1純虛函數(shù)4.2抽象類4.1純虛函數(shù)有時在基類中將某一成員函數(shù)定為虛函數(shù),并不是基類本身的要求,而是考慮到派生類的需要,在基類中預(yù)留了一個函數(shù)名,具體功能留給派生類根據(jù)需要去定義。例如在本章的例7.1程序中,基類Point中沒有求面積的area函數(shù),因為“點”是沒有面積的,也就是說,基類本身不需要這個函數(shù),所以在例7.1程序中的Point類中沒有定義area函數(shù)。但是,在其直接派生類Circle和間接派生類Cylinder中都需要有area函數(shù),而且這兩個area函數(shù)的功能不同,一個是求圓面積,一個是求圓柱體表面積。有的人自然會想到,在這種情況下應(yīng)當(dāng)將area聲明為虛函數(shù)。可以在基類Point中加一個area函數(shù),并聲明為虛函數(shù):virtualfloatarea()const{return0;}其返回值為0,表示“點”是沒有面積的。其實,在基類中并不使用這個函數(shù),其返回值也是沒有意義的。為簡化,可以不寫出這種無意義的函數(shù)體,只給出函數(shù)的原型,并在后面加上“=0”,如:virtualfloatarea()const=0;//純虛函數(shù)這就將area聲明為一個純虛函數(shù)(purevirtualfunction)。純虛函數(shù)是在聲明虛函數(shù)時被“初始化”為0的函數(shù)。聲明純虛函數(shù)的一般形式是:
virtual函數(shù)類型函數(shù)名(參數(shù)表列)=0;注意:純虛函數(shù)沒有函數(shù)體;最后面的“=0”并不表示函數(shù)返回值為0,它只起形式上的作用,告訴編譯系統(tǒng)“這是純虛函數(shù)”;這是一個聲明語句,最后應(yīng)有分號。純虛函數(shù)只有函數(shù)的名字而不具備函數(shù)的功能,不能被調(diào)用。它只是通知編譯系統(tǒng):“在這里聲明一個虛函數(shù),留待派生類中定義”。在派生類中對此函數(shù)提供定義后,它才能具備函數(shù)的功能,可被調(diào)用。純虛函數(shù)的作用是在基類中為其派生類保留一個函數(shù)的名字,以便派生類根據(jù)需要對它進(jìn)行定義。如果在基類中沒有保留函數(shù)名字,則無法實現(xiàn)多態(tài)性。如果在一個類中聲明了純虛函數(shù),而在其派生類中沒有對該函數(shù)定義,則該虛函數(shù)在派生類中仍然為純虛函數(shù)。4.2抽象類如果聲明了一個類,一般可以用它定義對象。但是在面向?qū)ο蟪绦蛟O(shè)計中,往往有一些類,它們不用來生成對象。定義這些類的惟一目的是用它作為基類去建立派生類。它們作為一種基本類型提供給用戶,用戶在這個基礎(chǔ)上根據(jù)自己的需要定義出功能各異的派生類。用這些派生類去建立對象。這種不用來定義對象而只作為一種基本類型用作繼承的類,稱為抽象類(abstractclass),由于它常用作基類,通常稱為抽象基類(abstractbaseclass)。凡是包含純虛函數(shù)的類都是抽象類。因為純虛函數(shù)是不能被調(diào)用的,包含純虛函數(shù)的類是無法建立對象的。抽象類的作用是作為一個類族的共同基類,或者說,為一個類族提供一個公共接口。如果在抽象類所派生出的新類中對基類的所有純虛函數(shù)進(jìn)行了定義,那么這些函數(shù)就被賦予了功能,可以被調(diào)用。這個派生類就不是抽象類,而是可以用來定義對象的具體類(concreteclass)。如果在派生類中沒有對所有純虛函數(shù)進(jìn)行定義,則此派生類仍然是抽象類,不能用來定義對象。雖然抽象類不能定義對象(或者說抽象類不能實例化),但是可以定義指向抽象類數(shù)據(jù)的指針變量。當(dāng)派生類成為具體類之后,就可以用這種指針指向派生類對象,然后通過該指針調(diào)用虛函數(shù),實現(xiàn)多態(tài)性的操作。(4.3)應(yīng)用實例例7.4虛函數(shù)和抽象基類的應(yīng)用。在本章例7.1介紹了以Point為基類的點—圓—圓柱體類的層次結(jié)構(gòu)。現(xiàn)在要對它進(jìn)行改寫,在程序中使用虛函數(shù)和抽象基類。類的層次結(jié)構(gòu)的頂層是抽象基類Shape(形狀)。Point(點),Circle(圓),Cylinder(圓柱體)都是Shape類的直接派生類和間接派生類。源碼參見7-4.cpp從本例可以進(jìn)一步明確以下結(jié)論:一個基類如果包含一個或一個以上純虛函數(shù),就是抽象基類。抽象基類不能也不必要定義對象。抽象基類與普通基類不同,它一般并不是現(xiàn)實存在的對象的抽象(例如圓形Circle就是千千萬萬個實際的圓的抽象),它可以沒有任何物理上的或其他實際意義方面的含義。在類的層次結(jié)構(gòu)中,頂層或最上面的幾層可以是抽象基類。抽象基類體現(xiàn)了本類族中各類的共性,把各類中共有的成員函數(shù)集中在抽象
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 特殊人群的科學(xué)運動與健康管理
- 幼兒園的德育教育工作方案5
- 環(huán)氧涂料行業(yè)的投資價值及風(fēng)險研究
- 手動葫蘆吊裝施工方案1
- 現(xiàn)代企業(yè)管理中的危機(jī)管理與領(lǐng)導(dǎo)力
- Module 1 Unit 1 Did you come back yesterday?(說課稿)-2024-2025學(xué)年外研版(三起)英語五年級上冊
- 1 古詩詞三首(說課稿)-2023-2024學(xué)年統(tǒng)編版語文四年級下冊001
- 2024年四年級英語上冊 Unit 2 My schoolbag The first period說課稿 人教PEP
- Unit 1 Science and Scientists Listening and Speaking說課稿+ 學(xué)案 高中英語同步備課系列人教版2019選擇性必修第二冊
- 金鎖記優(yōu)秀課件
- 人教版高中英語必修一單詞表(默寫版)
- 格式塔心理學(xué)與文藝心理學(xué)
- 海德堡HRT共焦激光角膜顯微鏡
- (汽車制造論文)機(jī)器人在汽車制造中應(yīng)用
- 幼兒園手工教學(xué)中教師指導(dǎo)行為研究-以自貢市幼兒園為例
- 初中物理實驗教學(xué)
- 《智能投顧 大數(shù)據(jù)智能驅(qū)動投顧創(chuàng)新》讀書筆記思維導(dǎo)圖
- 英語詞匯量測試附答案
- 企業(yè)應(yīng)急管理及能力提升培訓(xùn)課件精選
- 吲哚菁綠血管造影檢查知情同意書
評論
0/150
提交評論