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

下載本文檔

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

文檔簡(jiǎn)介

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

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

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

virtual

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

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

可以在程序運(yùn)行時(shí)通過(guò)調(diào)用相同的函數(shù)名而實(shí)現(xiàn)不同功能的函數(shù)稱(chēng)為虛函數(shù)。定義格式為:virtual<type>FuncName(<ArgList>);一旦把基類(lèi)的成員函數(shù)定義為虛函數(shù),由基類(lèi)所派生出來(lái)的所有派生類(lèi)中,該函數(shù)均保持虛函數(shù)的特性。在派生類(lèi)中重新定義基類(lèi)中的虛函數(shù)時(shí),可以不用關(guān)鍵字virtual來(lái)修飾這個(gè)成員函數(shù)。8虛函數(shù)是用關(guān)鍵字virtual修飾的某基類(lèi)中的protected或public成員函數(shù)。它可以在派生類(lèi)中重新定義,以形成不同版本。只有在程序的執(zhí)行過(guò)程中,依據(jù)指針具體指向哪個(gè)類(lèi)對(duì)象,或依據(jù)引用哪個(gè)類(lèi)對(duì)象,才能確定激活哪一個(gè)版本,實(shí)現(xiàn)動(dòng)態(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)用類(lèi)A的虛函數(shù)pa=&b;pa->print();//調(diào)用類(lèi)B的虛函數(shù)pa=&c;pa->print();}//調(diào)用類(lèi)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ù)兩個(gè)Set()函數(shù)參數(shù)不一致,是重載,不是虛函數(shù)11關(guān)于虛函數(shù),說(shuō)明以下幾點(diǎn):1、當(dāng)在基類(lèi)中把成員函數(shù)定義為虛函數(shù)后,在其派生類(lèi)中定義的虛函數(shù)必須與基類(lèi)中的虛函數(shù)同名,參數(shù)的類(lèi)型、順序、參數(shù)的個(gè)數(shù)必須一一對(duì)應(yīng),函數(shù)的返回的類(lèi)型也相同。若函數(shù)名相同,但參數(shù)的個(gè)數(shù)不同或者參數(shù)的類(lèi)型不同時(shí),則屬于函數(shù)的重載,而不是虛函數(shù)。若函數(shù)名不同,顯然這是不同的成員函數(shù)。122、實(shí)現(xiàn)這種動(dòng)態(tài)的多態(tài)性時(shí),必須使用基類(lèi)類(lèi)型的指針變量或基類(lèi)引用,并使該指針或引用指向不同的派生類(lèi)對(duì)象,并通過(guò)調(diào)用指針或引用所指向的虛函數(shù)才能實(shí)現(xiàn)動(dòng)態(tài)的多態(tài)性。xShow()xShow()yShow()xShow()zShow()類(lèi)A類(lèi)B類(lèi)CShow()定義為虛函數(shù)類(lèi)B與類(lèi)C均為類(lèi)A的公有派生。A*p;Bb;Cc;p=&b;p->Show();p=&c;p->Show();即在程序運(yùn)行時(shí),通過(guò)賦值語(yǔ)句實(shí)現(xiàn)多態(tài)性133、虛函數(shù)必須是類(lèi)的一個(gè)成員函數(shù),不能是友元函數(shù),也不能是靜態(tài)的成員函數(shù)。4、在派生類(lèi)中沒(méi)有重新定義虛函數(shù)時(shí),與一般的成員函數(shù)一樣,當(dāng)調(diào)用這種派生類(lèi)對(duì)象的虛函數(shù)時(shí),則調(diào)用其基類(lèi)中的虛函數(shù)。5、可把析構(gòu)函數(shù)定義為虛函數(shù),但是,不能將構(gòu)造函數(shù)定義為虛函數(shù)。146、虛函數(shù)與一般的成員函數(shù)相比較,調(diào)用時(shí)的執(zhí)行速度要慢一些。為了實(shí)現(xiàn)多態(tài)性,在每一個(gè)派生類(lèi)中均要保存相應(yīng)虛函數(shù)的入口地址表,函數(shù)的調(diào)用機(jī)制也是間接實(shí)現(xiàn)的。因此,除了要編寫(xiě)一些通用的程序,并一定要使用虛函數(shù)才能完成其功能要求外,通常不必使用虛函數(shù)。7、一個(gè)函數(shù)如果被定義成虛函數(shù),則不管經(jīng)歷多少次派生,仍將保持其虛特性,以實(shí)現(xiàn)“一個(gè)接口,多個(gè)形態(tài)”。15虛函數(shù)的訪問(wèn)用基類(lèi)指針或基類(lèi)引用訪問(wèn)與用派生類(lèi)對(duì)象名訪問(wèn)用基類(lèi)指針或基類(lèi)引用訪問(wèn)虛函數(shù)時(shí),指向其實(shí)際派生類(lèi)對(duì)象重新定義的函數(shù)。實(shí)現(xiàn)動(dòng)態(tài)聚束。通過(guò)一個(gè)派生類(lèi)對(duì)象名訪問(wèn)時(shí),只能靜態(tài)聚束。即由編譯器在編譯的時(shí)候決定調(diào)用哪個(gè)函數(shù)。16classPoint{floatx,y;public: Point(){} Point(floati,floatj){ x=i; y=j; }

virtual

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

floatarea(void) {returnPi*radius*radius;}//虛函數(shù)再定義};voidmain(void){Point*pp; //基類(lèi)指針,可以將派生類(lèi)對(duì)象的地址賦給基類(lèi)指針Circlec(5.4321);cout<<c.area()<<endl;cout<<c.Point::area()<<endl; cout<<c.Circle::area()<<endl;}輸出:92.7011 0 92.7011可見(jiàn),利用對(duì)象名進(jìn)行調(diào)用與一般非虛函數(shù)沒(méi)有區(qū)別。用對(duì)象名調(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私有派生,在類(lèi)外不能調(diào)用基類(lèi)函數(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ù)在基類(lèi)中不對(duì)虛函數(shù)給出有意義的實(shí)現(xiàn),它只是在派生類(lèi)中有具體的意義。這時(shí)基類(lèi)中的虛函數(shù)只是一個(gè)入口,具體的目的地由不同的派生類(lèi)中的對(duì)象決定。這個(gè)虛函數(shù)稱(chēng)為純虛函數(shù)。class<基類(lèi)名>{ virtual<類(lèi)型><函數(shù)名>(<參數(shù)表>)=0; ......};23classA{protected: intx;public: A(){x=1000;}

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

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

classC:publicA{//派生類(lèi) 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抽象類(lèi)不能定義抽象類(lèi)的對(duì)象241、在定義純虛函數(shù)時(shí),不能定義虛函數(shù)的實(shí)現(xiàn)部分。2、把函數(shù)名賦于0,本質(zhì)上是將指向函數(shù)體的指針值賦為初值0。與定義空函數(shù)不一樣,空函數(shù)的函數(shù)體為空,即調(diào)用該函數(shù)時(shí),不執(zhí)行任何動(dòng)作。在沒(méi)有重新定義這種純虛函數(shù)之前,是不能調(diào)用這種函數(shù)的。253、把至少包含一個(gè)純虛函數(shù)的類(lèi),稱(chēng)為抽象類(lèi)。這種類(lèi)只能作為派生類(lèi)的基類(lèi),不能用來(lái)說(shuō)明這種類(lèi)的對(duì)象。其理由是明顯的:因?yàn)樘摵瘮?shù)沒(méi)有實(shí)現(xiàn)部分,所以不能產(chǎn)生對(duì)象。但可以定義指向抽象類(lèi)的指針,即指向這種基類(lèi)的指針。當(dāng)用這種基類(lèi)指針指向其派生類(lèi)的對(duì)象時(shí),必須在派生類(lèi)中重載純虛函數(shù),否則會(huì)產(chǎn)生程序的運(yùn)行錯(cuò)誤。264、在以抽象類(lèi)作為基類(lèi)的派生類(lèi)中必須有純虛函數(shù)的實(shí)現(xiàn)部分,即必須有重載純虛函數(shù)的函數(shù)體。否則,這樣的派生類(lèi)也是不能產(chǎn)生對(duì)象的。綜上所述,可把純虛函數(shù)歸結(jié)為:抽象類(lèi)的唯一用途是為派生類(lèi)提供基類(lèi),純虛函數(shù)的作用是作為派生類(lèi)中的成員函數(shù)的基礎(chǔ),并實(shí)現(xiàn)動(dòng)態(tài)多態(tài)性。27虛基類(lèi)多基派生中的多條路徑具有公共基類(lèi)時(shí),在這條路徑的匯合處就會(huì)因?qū)不?lèi)產(chǎn)生多個(gè)拷貝而產(chǎn)生同名函數(shù)調(diào)用的二義性。解決這個(gè)問(wèn)題的辦法就是把公共基類(lèi)定義為虛基類(lèi),使由它派生的多條路徑的匯聚處只產(chǎn)生一個(gè)拷貝。classBase{};classA:publicBase{};classB:publicBase{};classC:publicA,publicB{};類(lèi)C中繼承了兩個(gè)類(lèi)Base,即有兩個(gè)類(lèi)Base的實(shí)現(xiàn)部分,在調(diào)用時(shí)產(chǎn)生了二義性。28用虛基類(lèi)進(jìn)行多重派生時(shí),若虛基類(lèi)沒(méi)有缺省的構(gòu)造函數(shù),則在每一個(gè)派生類(lèi)的構(gòu)造函數(shù)中都必須有對(duì)虛基類(lèi)構(gòu)造函數(shù)的調(diào)用(且首先調(diào)用)。由虛基類(lèi)派生出的對(duì)象初始化時(shí),直接調(diào)用虛基類(lèi)的構(gòu)造函數(shù)。因此,若將一個(gè)類(lèi)定義為虛基類(lèi),則一定有正確的構(gòu)造函數(shù)可供所有派生類(lèi)調(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;//錯(cuò)誤A*pa=&cc;pa->a();pa->b();pa->c();pa->d();pa->e();pa->f();}將類(lèi)C的地址賦值時(shí)產(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;//錯(cuò)誤A*pa=&cc;pa->a();pa->b();pa->c();pa->d();pa->e();pa->f();}將類(lèi)C的地址賦值時(shí)產(chǎn)生歧義類(lèi)C中有兩個(gè)base,只有一個(gè)Aa()inCb()inAc()inbased()inCe()inbasef()inA為避免這種情況,將base定義為虛基類(lèi)。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. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶(hù)所有。
  • 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ì)用戶(hù)上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶(hù)上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶(hù)因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論