C++備課講義第14章_第1頁
C++備課講義第14章_第2頁
C++備課講義第14章_第3頁
C++備課講義第14章_第4頁
C++備課講義第14章_第5頁
已閱讀5頁,還剩31頁未讀, 繼續(xù)免費閱讀

下載本文檔

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

文檔簡介

第14章虛函數(shù)1虛函數(shù)多態(tài)性:調(diào)用同一個函數(shù)名,可以根據(jù)需要但實現(xiàn)不同的功能。多態(tài)性是面向?qū)ο蟮某绦蛟O(shè)計的關(guān)鍵技術(shù)。編譯時的多態(tài)性(函數(shù)重載)運行時的多態(tài)性(虛函數(shù))多態(tài)性運行時的多態(tài)性是指在程序執(zhí)行之前,根據(jù)函數(shù)名和參數(shù)無法確定應該調(diào)用哪一個函數(shù),必須在程序的執(zhí)行過程中,根據(jù)具體的執(zhí)行情況來動態(tài)地確定2可以將一個派生類對象的地址賦給基類的指針變量?;悓ο笈缮悓ο驜aseb;Derived;Base*basep;basepbasep=&b;basepbasep=&d;basep只能引用從基類繼承來的成員。xShow()xShow()yShow()basep->Show();basep->Show()基類指針派生類對象基類對象3classPoint{ floatx,y;public: Point(){} Point(floati,floatj){ x=i; y=j; }

floatarea(void) { return0.0; }};constfloatPi=3.14159;classCircle:publicPoint{ //類Point的派生類 floatradius;public: Circle(floatr){ radius=r; }

floatarea(void) {returnPi*radius*radius; }};voidmain(void){Point*pp; //基類指針,可以將派生類對象的地址賦給基類指針Circlec(5.4321);pp=&c;cout<<pp->area()<<endl; //調(diào)用的是基類中有的公有函數(shù)}在基類和派生類中具有相同的公有函數(shù)area()。在這種情況下,使用基類的指針時,只能訪問從相應基類中繼承來的成員,而不允許訪問在派生類中增加的成員。輸出為04基類對象派生類對象Baseb;Derived;basepbasepxShow()xShow()yShow()basep->Show()Base*basep;basep=&b;basep=&d;basep->Show();即指向派生類新增的成員函數(shù)需要將基類中的Show()說明為虛函數(shù)5若要訪問派生類中相同名字的函數(shù),必須將基類中的同名函數(shù)定義為虛函數(shù),這樣,將不同的派生類對象的地址賦給基類的指針變量后,就可以動態(tài)地根據(jù)這種賦值語句調(diào)用不同類中的函數(shù)。6classPoint{floatx,y;public: Point(){} Point(floati,floatj){ x=i; y=j; }

virtual

floatarea(void) {return0.0;}};constfloatPi=3.14159;classCircle:publicPoint{ //類Point的派生類 floatradius;public: Circle(floatr){ radius=r; }

floatarea(void) {returnPi*radius*radius;}};voidmain(void){Point*pp; //基類指針,可以將派生類對象的地址賦給基類指針Circlec(5.4321);pp=&c;cout<<pp->area()<<endl;//調(diào)用虛函數(shù)}將area()聲明為虛函數(shù),編譯器對其進行動態(tài)聚束,按照實際對象c調(diào)用了Circle中的函數(shù)area()。使Point類中的area()與Circle類中的area()有一個統(tǒng)一的接口。輸出:92.7011聲明為虛函數(shù)調(diào)用虛函數(shù)虛函數(shù)再定義7虛函數(shù)的定義和使用

可以在程序運行時通過調(diào)用相同的函數(shù)名而實現(xiàn)不同功能的函數(shù)稱為虛函數(shù)。定義格式為:virtual<type>FuncName(<ArgList>);一旦把基類的成員函數(shù)定義為虛函數(shù),由基類所派生出來的所有派生類中,該函數(shù)均保持虛函數(shù)的特性。在派生類中重新定義基類中的虛函數(shù)時,可以不用關(guān)鍵字virtual來修飾這個成員函數(shù)。8虛函數(shù)是用關(guān)鍵字virtual修飾的某基類中的protected或public成員函數(shù)。它可以在派生類中重新定義,以形成不同版本。只有在程序的執(zhí)行過程中,依據(jù)指針具體指向哪個類對象,或依據(jù)引用哪個類對象,才能確定激活哪一個版本,實現(xiàn)動態(tài)聚束。9classA{protected: intx;public: A(){x=1000;}

virtualvoidprint(){ cout<<“x=”<<x<<‘\t’; }//虛函數(shù)};classB:publicA{ inty;public: B(){y=2000;}

voidprint(){ cout<<“y=”<<y<<‘\t’; }//派生虛函數(shù)}; classC:publicA{ intz;public: C(){z=3000;}

voidprint(){ cout<<“z=”<<z<<‘\n’; }//派生虛函數(shù)};voidmain(void){Aa,*pa;Bb; Cc;a.print();b.print(); c.print();//靜態(tài)調(diào)用pa=&a;pa->print();//調(diào)用類A的虛函數(shù)pa=&b;pa->print();//調(diào)用類B的虛函數(shù)pa=&c;pa->print();}//調(diào)用類C的虛函數(shù)x=1000y=2000z=3000x=1000y=2000z=300010classBase{public:virtualintSet(inta,intb){.....}....};classDerive:publicBase{public:intSet(intx,inty){.....}.....};classBase{public:virtualintSet(inta,intb){.....}....};classDerive:publicBase{public:intSet(intx,inty=0){.....}.....};intSet(int,int)是虛函數(shù)兩個Set()函數(shù)參數(shù)不一致,是重載,不是虛函數(shù)11關(guān)于虛函數(shù),說明以下幾點:1、當在基類中把成員函數(shù)定義為虛函數(shù)后,在其派生類中定義的虛函數(shù)必須與基類中的虛函數(shù)同名,參數(shù)的類型、順序、參數(shù)的個數(shù)必須一一對應,函數(shù)的返回的類型也相同。若函數(shù)名相同,但參數(shù)的個數(shù)不同或者參數(shù)的類型不同時,則屬于函數(shù)的重載,而不是虛函數(shù)。若函數(shù)名不同,顯然這是不同的成員函數(shù)。122、實現(xiàn)這種動態(tài)的多態(tài)性時,必須使用基類類型的指針變量或基類引用,并使該指針或引用指向不同的派生類對象,并通過調(diào)用指針或引用所指向的虛函數(shù)才能實現(xiàn)動態(tài)的多態(tài)性。xShow()xShow()yShow()xShow()zShow()類A類B類CShow()定義為虛函數(shù)類B與類C均為類A的公有派生。A*p;Bb;Cc;p=&b;p->Show();p=&c;p->Show();即在程序運行時,通過賦值語句實現(xiàn)多態(tài)性133、虛函數(shù)必須是類的一個成員函數(shù),不能是友元函數(shù),也不能是靜態(tài)的成員函數(shù)。4、在派生類中沒有重新定義虛函數(shù)時,與一般的成員函數(shù)一樣,當調(diào)用這種派生類對象的虛函數(shù)時,則調(diào)用其基類中的虛函數(shù)。5、可把析構(gòu)函數(shù)定義為虛函數(shù),但是,不能將構(gòu)造函數(shù)定義為虛函數(shù)。146、虛函數(shù)與一般的成員函數(shù)相比較,調(diào)用時的執(zhí)行速度要慢一些。為了實現(xiàn)多態(tài)性,在每一個派生類中均要保存相應虛函數(shù)的入口地址表,函數(shù)的調(diào)用機制也是間接實現(xiàn)的。因此,除了要編寫一些通用的程序,并一定要使用虛函數(shù)才能完成其功能要求外,通常不必使用虛函數(shù)。7、一個函數(shù)如果被定義成虛函數(shù),則不管經(jīng)歷多少次派生,仍將保持其虛特性,以實現(xiàn)“一個接口,多個形態(tài)”。15虛函數(shù)的訪問用基類指針或基類引用訪問與用派生類對象名訪問用基類指針或基類引用訪問虛函數(shù)時,指向其實際派生類對象重新定義的函數(shù)。實現(xiàn)動態(tài)聚束。通過一個派生類對象名訪問時,只能靜態(tài)聚束。即由編譯器在編譯的時候決定調(diào)用哪個函數(shù)。16classPoint{floatx,y;public: Point(){} Point(floati,floatj){ x=i; y=j; }

virtual

floatarea(void) {return0.0;}//聲明為虛函數(shù)};constfloatPi=3.14159;classCircle:publicPoint{ //類Point的派生類 floatradius;public: Circle(floatr){ radius=r; }

floatarea(void) {returnPi*radius*radius;}//虛函數(shù)再定義};voidmain(void){Point*pp; //基類指針,可以將派生類對象的地址賦給基類指針Circlec(5.4321);cout<<c.area()<<endl;cout<<c.Point::area()<<endl; cout<<c.Circle::area()<<endl;}輸出:92.7011 0 92.7011可見,利用對象名進行調(diào)用與一般非虛函數(shù)沒有區(qū)別。用對象名調(diào)用area()17classbase0{public: voidv(void){ cout<<"base0\n"; }};classbase1:publicbase0{public: virtualvoidv(void){cout<<"base1\n";}};classA1:publicbase1{public: voidv(){ cout<<"A1\n"; }};classA2:publicA1{public: voidv(void){ cout<<"A2\n"; }};classB1:privatebase1{public: voidv(void){ cout<<"B1\n"; }};classB2:publicB1{public: voidv(void){ cout<<"B2\n"; }};voidmain(void){base0*pb;A1a1;(pb=&a1)->v();A2a2;(pb=&a2)->v();B1b1;

(pb=&b1)->v();B2b2;

(pb=&b2)->v();}base0base0私有派生,在類外不能調(diào)用基類函數(shù)18classbase0{public: voidv(void){ cout<<"base0\n"; }};classbase1:publicbase0{public: virtualvoidv(void){cout<<"base1\n";}};classA1:publicbase1{public: voidv(){ cout<<"A1\n"; }};classA2:publicA1{public: voidv(void){ cout<<"A2\n"; }};classB1:privatebase1{public: voidv(void){ cout<<"B1\n"; }};classB2:publicB1{public: voidv(void){ cout<<"B2\n"; }};voidmain(void){base1*pb;A1a1;(pb=&a1)->v();A2a2;(pb=&a2)->v();}A1A219下面程序的輸出是

。classA{protected:intx;public:A(){x=1000;}virtualvoidp(){cout<<"x="<<x<<'\n';p2();}virtualvoidp2(){cout<<"A::p2()"<<endl;}};classC:publicA{ intz;public:C(){z=3000;}voidp(){cout<<"z="<<z<<'\n';p2();}virtualvoidp2(){cout<<"C::p2()"<<endl;}};voidmain(void){Cc;Aa,*pa=&a;pa->p();pa=&c;pa->p();}x=1000z=3000C::p2()A::p2()20classB0{public:virtualvoiddisplay(){cout<<"B0::display()\n";}};classB1:publicB0{public:voiddisplay(){cout<<"B1::display()\n";}};classD:publicB1{public:voiddisplay(){cout<<"D::display()\n";}};voidfun(B0*p){p->display();}voidmain(){B0b0,*p;p=&b0;fun(p);B1b1;p=&b1;fun(p);Dd;p=&d;fun(p);}B0::display()B1::display()D::display()21classB0{public:virtualvoiddisplay(){cout<<"B0::display()\n";}};classB1:publicB0{public:voiddisplay(){cout<<"B1::display()\n";}};classD:publicB1{public:voiddisplay(){cout<<"D::display()\n";}};voidfun(B0&bb){bb.display();}voidmain(){B0b0;fun(b0);B1b1;fun(b1);Dd;fun(d);}B0::display()B1::display()D::display()22純虛函數(shù)在基類中不對虛函數(shù)給出有意義的實現(xiàn),它只是在派生類中有具體的意義。這時基類中的虛函數(shù)只是一個入口,具體的目的地由不同的派生類中的對象決定。這個虛函數(shù)稱為純虛函數(shù)。class<基類名>{ virtual<類型><函數(shù)名>(<參數(shù)表>)=0; ......};23classA{protected: intx;public: A(){x=1000;}

virtualvoidprint()=0;//定義純虛函數(shù)};classB:publicA{//派生類private:inty;public: B(){y=2000;}

voidprint(){cout<<“y=”<<y<<‘\n’;}//重新定義純虛函數(shù)};

classC:publicA{//派生類 intz;public: C(){z=3000;}

voidprint(){cout<<“z=”<<z<<‘\n’;}//重新定義純虛函數(shù)};voidmain(void){A*pa; Bb; Cc;pa=&b;pa->print(); pa=&c;pa->print();

Aa;pa=&a;pa->print();}y=2000z=3000抽象類不能定義抽象類的對象241、在定義純虛函數(shù)時,不能定義虛函數(shù)的實現(xiàn)部分。2、把函數(shù)名賦于0,本質(zhì)上是將指向函數(shù)體的指針值賦為初值0。與定義空函數(shù)不一樣,空函數(shù)的函數(shù)體為空,即調(diào)用該函數(shù)時,不執(zhí)行任何動作。在沒有重新定義這種純虛函數(shù)之前,是不能調(diào)用這種函數(shù)的。253、把至少包含一個純虛函數(shù)的類,稱為抽象類。這種類只能作為派生類的基類,不能用來說明這種類的對象。其理由是明顯的:因為虛函數(shù)沒有實現(xiàn)部分,所以不能產(chǎn)生對象。但可以定義指向抽象類的指針,即指向這種基類的指針。當用這種基類指針指向其派生類的對象時,必須在派生類中重載純虛函數(shù),否則會產(chǎn)生程序的運行錯誤。264、在以抽象類作為基類的派生類中必須有純虛函數(shù)的實現(xiàn)部分,即必須有重載純虛函數(shù)的函數(shù)體。否則,這樣的派生類也是不能產(chǎn)生對象的。綜上所述,可把純虛函數(shù)歸結(jié)為:抽象類的唯一用途是為派生類提供基類,純虛函數(shù)的作用是作為派生類中的成員函數(shù)的基礎(chǔ),并實現(xiàn)動態(tài)多態(tài)性。27虛基類多基派生中的多條路徑具有公共基類時,在這條路徑的匯合處就會因?qū)不惍a(chǎn)生多個拷貝而產(chǎn)生同名函數(shù)調(diào)用的二義性。解決這個問題的辦法就是把公共基類定義為虛基類,使由它派生的多條路徑的匯聚處只產(chǎn)生一個拷貝。classBase{};classA:publicBase{};classB:publicBase{};classC:publicA,publicB{};類C中繼承了兩個類Base,即有兩個類Base的實現(xiàn)部分,在調(diào)用時產(chǎn)生了二義性。28用虛基類進行多重派生時,若虛基類沒有缺省的構(gòu)造函數(shù),則在每一個派生類的構(gòu)造函數(shù)中都必須有對虛基類構(gòu)造函數(shù)的調(diào)用(且首先調(diào)用)。由虛基類派生出的對象初始化時,直接調(diào)用虛基類的構(gòu)造函數(shù)。因此,若將一個類定義為虛基類,則一定有正確的構(gòu)造函數(shù)可供所有派生類調(diào)用。29classbase{public:virtualvoida(){ cout<<"a()inbase\n";}virtualvoidb(){ cout<<"b()inbase\n";}virtualvoidc(){ cout<<"c()inbase\n";}virtualvoidd(){ cout<<"d()inbase\n";}virtualvoide(){ cout<<"e()inbase\n";}virtualvoidf(){ cout<<"f()inbase\n";}};classA:publicbase{public: virtualvoida(){ cout<<"a()inA\n";}virtualvoidb(){ cout<<"b()inA\n";}virtualvoidf(){ cout<<"f()inA\n";}};classB:publicbase{public:virtualvoida(){ cout<<"a()inB\n";}virtualvoidb(){ cout<<"b()inB\n";}virtualvoidc(){ cout<<"c()inB\n";}};classC:publicA,publicB{public: virtualvoida(){ cout<<"a()inC\n";}virtualvoidd(){ cout<<"d()inC\n";}};voidmain(void){Ccc;

base*pbase=&cc;//錯誤A*pa=&cc;pa->a();pa->b();pa->c();pa->d();pa->e();pa->f();}將類C的地址賦值時產(chǎn)生歧義30a()b()c()d()e()f()a()b()c()d()e()f()a()b()f()a()b()c()d()e()f()a()c()a()b()c()d()e()f()a()b()f()a()b()c()d()e()f()a()c()baseABCa()d()AB31classbase{public:virtualvoida(){ cout<<"a()inbase\n";}virtualvoidb(){ cout<<"b()inbase\n";}virtualvoidc(){ cout<<"c()inbase\n";}virtualvoidd(){ cout<<"d()inbase\n";}virtualvoide(){ cout<<"e()inbase\n";}virtualvoidf(){ cout<<"f()inbase\n";}};classA:publicbase{public: virtualvoida(){ cout<<"a()inA\n";}virtualvoidb(){ cout<<"b()inA\n";}virtualvoidf(){ cout<<"f()inA\n";}};classB:publicbase{public:virtualvoida(){ cout<<"a()inB\n";}virtualvoidb(){ cout<<"b()inB\n";}virtualvoidc(){ cout<<"c()inB\n";}};classC:publicA,publicB{public: virtualvoida(){ cout<<"a()inC\n";}virtualvoidd(){ cout<<"d()inC\n";}};voidmain(void){Ccc;

base*pbase=&cc;//錯誤A*pa=&cc;pa->a();pa->b();pa->c();pa->d();pa->e();pa->f();}將類C的地址賦值時產(chǎn)生歧義類C中有兩個base,只有一個Aa()inCb()inAc()inbased()inCe()inbasef()inA為避免這種情況,將base定義為虛基類。32classbase{public:virtualvoida(){ cout<<"a()inbase\n";}virtualvoidb(){ cout<<"b()inbase\n";}virtualvoidc(){ cout<<"c()inbase\n";}virtualvoidd(){ cout<<"d()inbase\n";}virtualvoide(){ cout<<"e()inbase\n";}virtualvoidf(){ cout<<"f()inbase\n";}};classA:virtualpublicbase{public: virtualvoida(){ cout<<"a()inA\n";}virtualvoidb(){ cout<<"b()inA\n";}virtualvoidf(){ cout<<"f()inA\n";}};classB:virtualpublicbase{public:virtualvoida(){ cout<<"a()inB\n";}virtualvoidc(){ cout<<"c()inB\n";}};classC:publicA,publicB{public: virtualvoida(){ cout<<"a()inC\n";}virtualvoidd(){ cout<<"d()inC\n";}};voidmain(void){Ccc;base*pa=&cc;pa->a();pa->b();pa->c();pa->d();pa->e();pa->f();}33a()b()c()d()e()f()a()b()c()d()e()f()a()b()f()a()b()c()d()e()f()a()c()a()b()c()d()e()f()a()b()f()a()c()baseABCa()d()AB34classbase{public:virtualvoida(){ cout<<"a()inbase\n";}virtualvoidb(){ cout<<"b()inbase\n";}virtualvoidc(){ cout<<"c()inbase\n";}virtualvoidd(){ cout<<"d()inbase\n";}virtualvoide(){ cout<<"e()inbase\n";}virtualvoidf(){ cout<<"f()inbase\n";}};classA:virtualpublicbase{public: virtualvoida(){ cout<<"a()inA\n";}virtualvoidb(){ cout<<"b()inA\n

溫馨提示

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

最新文檔

評論

0/150

提交評論