《CC++語言程序設(shè)計案例教程》課件-第11章多態(tài)性與虛函數(shù)_第1頁
《CC++語言程序設(shè)計案例教程》課件-第11章多態(tài)性與虛函數(shù)_第2頁
《CC++語言程序設(shè)計案例教程》課件-第11章多態(tài)性與虛函數(shù)_第3頁
《CC++語言程序設(shè)計案例教程》課件-第11章多態(tài)性與虛函數(shù)_第4頁
《CC++語言程序設(shè)計案例教程》課件-第11章多態(tài)性與虛函數(shù)_第5頁
已閱讀5頁,還剩55頁未讀, 繼續(xù)免費閱讀

下載本文檔

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

文檔簡介

1第十一章多態(tài)性與虛函數(shù)

C

語言程序設(shè)計案例教程12/23/20242案例一函數(shù)重載1.問題描述設(shè)計程序?qū)崿F(xiàn)同一個函數(shù)Area根據(jù)提供參數(shù)的個數(shù)不同,分別實現(xiàn)圓和矩形面積的計算。2.問題分析注意本案例中函數(shù)重載時的依據(jù)是什么,函數(shù)的類型、參數(shù)個數(shù)、參數(shù)類型等的不同。本例中函數(shù)重載(計算面積的函數(shù),輸入一個數(shù),視為圓形;輸入兩個數(shù),視為矩形,分別計算面積)12/23/202433.?C++?代碼#include<iostream.h>#definePI3.14floatArea(floatr){returnPI*r*r;}floatArea(floata,floatb){returna*b;}voidmain(){floatr,a,b; //r作為圓的半徑,a、b分別作為矩形的長和寬。

cout<<"請輸入圓的半徑";cin>>r;cout<<"請輸入矩形的長:";cin>>a;cout<<"請輸入矩形的寬:";cin>>b;cout<<"圓的面積為:"<<Area(r)<<"矩形的面積為:"<<Area(a,b)<<endl;//Area(r)和Area(a,b)參數(shù)個數(shù)和類型不同}4.程序運行結(jié)果請輸入圓的半徑19請輸入矩形的長:5請輸入矩形的寬:4圓的面積為:1133.54矩形的面積為:2012/23/2024411.1多?態(tài)?性?概?述

用同一個名字來訪問不同函數(shù)的性質(zhì)被稱作多態(tài)性。也就是說不同對象收到相同的消息時,產(chǎn)生不同的動作,比如讓狗和魚不同的兩個對象移動,則狗會走,魚會游。使用多態(tài)性,一些相似功能的函數(shù)可用同一個名字來定義,這不僅使得概念上清晰,還可達(dá)到動態(tài)鏈接的目的,實現(xiàn)運行時的多態(tài)性。在C++?中,多態(tài)性的實現(xiàn)和聯(lián)編這一概念有關(guān)。一個源程序經(jīng)過編譯、鏈接,成為可執(zhí)行文件的過程就是聯(lián)編。聯(lián)編分為兩類:靜態(tài)聯(lián)編和動態(tài)聯(lián)編。靜態(tài)聯(lián)編,也稱前期聯(lián)編,是指在運行之前就完成的編譯;動態(tài)聯(lián)編,也稱后期聯(lián)編,是指在程序運行時才完成的編譯。靜態(tài)聯(lián)編支持的多態(tài)性稱為編譯時多態(tài)性,也稱靜態(tài)多態(tài)性。在C++?中,編譯時多態(tài)性是通過函數(shù)重載和運算符重載實現(xiàn)的。動態(tài)聯(lián)編支持的多態(tài)性稱為運行時多態(tài)性,也稱動態(tài)多態(tài)性。在C++?中,運行時多態(tài)性是通過繼承和虛函數(shù)來實現(xiàn)的。12/23/2024511.2函數(shù)重載

編譯時的多態(tài)性可以通過函數(shù)重載來實現(xiàn)。函數(shù)重載有兩種情況:一是參數(shù)有所差別的重載,意義在于它能用同一個名字訪問一組相關(guān)的函數(shù),在前面我們已經(jīng)做過介紹;再一個是函數(shù)所帶參數(shù)完全相同,只是它們屬于不同的類,這些類之間一般有繼承和派生關(guān)系。12/23/20246例11.1在基類和派生類中函數(shù)重載。#include<iostream.h>classBase{//基類

intx,y;public:Base(inta,intb){x=a;y=b;}voiddisplay()//基類中的display()函數(shù)

{cout<<"執(zhí)行基類中的display()函數(shù)"<<endl;cout<<x<<","<<y<<endl;}};classDerived:publicBase{//派生類Derived,Base公有派生出Derived類

intz;public:Derived(inta,intb,intc):Base(a,b){z=c;}voiddisplay()//派生類中的display()函數(shù)

{cout<<"執(zhí)行派生類中的display()函數(shù)"<<endl;cout<<z<<endl;}};12/23/20247main(){Baseb(20,20);Derivedd(8,8,30);b.display();//執(zhí)行基類中的display()函數(shù)

d.display();//執(zhí)行派生類中的display()函數(shù)

d.Base::display();//執(zhí)行基類中的display()函數(shù)}在基類和派生類中進(jìn)行函數(shù)重載時,用以下兩種方法可以在編譯時區(qū)別重載函數(shù):(1)使用對象名加以區(qū)分。例如:b.display()和d.display()分別調(diào)用類Base和Derived的display()函數(shù)。派生類中會隱藏基類中與派生類同名的成員。(2)使用“類名::”加以區(qū)分。例如:d.Base::display()調(diào)用的是Base的display()函數(shù)。12/23/20248案例二運算符重載1.問題描述將運算符“+”重載為復(fù)數(shù)的成員函數(shù)(復(fù)數(shù)的加法運算)。2.問題分析注意掌握運算符如何重載為類的成員函數(shù)方式以及運算符的調(diào)用方式。12/23/202493.?C++?代碼#include<iostream.h>classcomplex{public:doublereal;doubleimag;complex(doubler=0,doublei=0) //complex的構(gòu)造函數(shù)

{real=r;imag=i;}};complexoperator+(complexco1,complexco2) //運算符+重載為類的成員函數(shù){complextemp;temp.real=co1.real+co2.real;temp.imag=co1.imag+co2.imag;returntemp;}main(){complexcom1(3.1,2.4),com2(1.3,5.3),total1,total2;total1=operator+(com1,com2); //調(diào)用運算符函數(shù)operater+()的第一種方式

cout<<"real1="<<total1.real<<""<<"imag1="<<total1.imag<<endl;total2=com1+com2; //調(diào)用運算符函數(shù)operater+()的第二種方式

cout<<"real2="<<total2.real<<""<<"imag2="<<total2.imag<<endl;}4.程序運行結(jié)果real1=4.4imag1=7.7real2=4.4imag2=7.712/23/20241011.3運算符的重載在C++?中,除了可以對函數(shù)重載外,還可以對大多數(shù)運算符實現(xiàn)重載。自定義的類運算往往用運算符重載函數(shù)來實現(xiàn)。運算符重載可以擴(kuò)充語言的功能,就是將運算符擴(kuò)充到用戶定義的類型上去。運算符重載通過創(chuàng)建運算符函數(shù)operator()來實現(xiàn)。為了操作相應(yīng)類的所有數(shù)據(jù)成員,可以將運算符重載成類的成員函數(shù),也可以是類的友元函數(shù)。12/23/20241111.3.1運算符重載的規(guī)則如果有一個復(fù)數(shù)類complex:classcomplex{public:doublereal;

doubleimag;complex(doubler=0,doublei=0){real=r;imag=i;}};若要把complex的兩個對象相加,下面的語句是不能實現(xiàn)的:complexobj1(3.1,2.2),obj2(1.3,4.2),total;total=obj1+obj2;//錯誤12/23/202412不能實現(xiàn)的原因是類complex的類型不是基本數(shù)據(jù)類型,而是用戶自定義的數(shù)據(jù)類型,“+”只能實現(xiàn)基本數(shù)據(jù)類型的加法運算,C++?無法直接將兩個自定義的類對象進(jìn)行相加。為了表達(dá)方便,人們希望能對自定義的類型進(jìn)行運算,希望內(nèi)部運算符(如“+”“-”“*”“/”等)在特定的類對象上以新的含義進(jìn)行解釋,即實現(xiàn)運算符的重載。C++?為運算符重載提供了一種方法,使用以下形式進(jìn)行運算符重載:typeoperator@(參數(shù)表);其中,@表示要重載的運算符,type是返回類型。12/23/202413說明:(1)除了“.”“*”“::”“?:”“#”“##”,其他運算符都可以重載。(2)?delete、new、指針、引用也可以重載。(3)運算符函數(shù)可以定義為內(nèi)置函數(shù)。(4)用戶定義的運算符無法改變運算符原有的優(yōu)先次序。(5)不可以定義系統(tǒng)定義的運算符集之外的運算符。(6)不能改變運算符的語法結(jié)構(gòu)。在案例二中可以看出,我們定義了一個operator+()函數(shù)實現(xiàn)了complex對象的相加。在調(diào)用該函數(shù)時,可以采用顯式調(diào)用和隱式調(diào)用。operator+()函數(shù)是一個非類成員,所以將complex類的數(shù)據(jù)成員real和imag定義成了公有函數(shù),以方便operator+()函數(shù)的訪問。這樣破壞了complex的數(shù)據(jù)私有性。因此在重載運算符時通常重載為類的成員函數(shù)或友元函數(shù)。12/23/20241411.3.2運算符重載為成員函數(shù)

成員運算符定義的兩種語法形式如下:classX{//…typeoperator@(參數(shù)表);

//…};typeX::operator@(參數(shù)表){//函數(shù)體}其中,type為函數(shù)的返回類型,@為所要重載的運算符符號,X是重載此運算符的類名,參數(shù)表中羅列的是該運算符所需要的操作數(shù),和一般函數(shù)的形參定義一致。在成員運算符函數(shù)的參數(shù)表中,若運算符是單目的,則參數(shù)表為空(調(diào)用成員函數(shù)的對象作為操作數(shù));若運算符是雙目的,則參數(shù)表中有一個操作數(shù)(調(diào)用成員函數(shù)的對象作為另一個操作數(shù))。12/23/2024151.雙目運算符重載對雙目運算符而言,成員運算符函數(shù)的參數(shù)表中只有一個參數(shù),它作為運算符的右操作數(shù),此時當(dāng)前對象作為運算符的左操作數(shù),它是通過this指針隱含地傳遞給函數(shù)的。調(diào)用時可采用以下兩種方式:A@B;//隱式調(diào)用,AB都是運算符操作對象A.operator@(B);//顯式調(diào)用下面是一個雙目運算符的例子。12/23/202416例11.2雙目運算符重載為成員函數(shù)。#include<iostream.h>classcomplex{private:doublereal;doubleimag;public:complex(doubler=0.0,doublei=0.0);complexoperator+(complexc); //重載“+”運算符為復(fù)數(shù)成員函數(shù)

complexoperator-(complexc); //重載“-”運算符為復(fù)數(shù)成員函數(shù)

voiddisplay();};complex::complex(doubler,doublei){real=r;imag=i;}complexcomplex::operator+(complexc){complextemp;temp.real=real+c.real;temp.imag=imag+c.imag;returntemp;}12/23/202417complexcomplex::operator-(complexc){complextemp;temp.real=real-c.real;temp.imag=imag-c.imag;returntemp;}voidcomplex::display(){cout<<real;if(imag>0)cout<<"+";if(imag!=0)cout<<imag<<"i\n";}main(){complexA1(1.9,6.9),A2(0.9,0.9),A3,A4,A5,A6;A3=A1+A2; //對operator+()隱式調(diào)用

A4=A1-A2; //對operator-()隱式調(diào)用

A1.display();A2.display();A3.display();A4.display();}程序運行結(jié)果如下:1.9+6.9i0.9+0.9i2.8+7.8i1+6i12/23/2024182.單目運算符重載對單目運算符而言,成員運算符函數(shù)的參數(shù)表中沒有參數(shù),此時當(dāng)前對象作為運算符的一個操作數(shù)。調(diào)用時可采用以下兩種方式:@A; //隱式調(diào)用A.operator@(); //顯式調(diào)用下面是一個單目運算符的例子。12/23/202419例11.3單目運算符重載為成員函數(shù)。#include<iostream.h>classMyClass{intx,y;public:MyClass(inti=0,intj=0);voidprint();MyClassoperator++(); //定義單目運算符函數(shù)};MyClass::MyClass(inti,intj){x=i;y=j;}voidMyClass::print(){cout<<"x:"<<x<<",y:"<<y<<endl;}MyClassMyClass::operator++() //前置++{++x;++y;return*this;}12/23/202420main(){MyClassob(10,20);ob.print();++ob; //隱式調(diào)用

ob.print();ob.operator++(); //顯式調(diào)用

ob.print();}程序運行結(jié)果如下:x:10,y:20x:11,y:21x:12,y:2212/23/20242111.3.3運算符重載為友元函數(shù)

在C++?中,還可以把運算符函數(shù)定義成某個類的友元函數(shù),稱為友元運算符函數(shù)。1.友元運算符函數(shù)定義的語法形式友元運算符函數(shù)在類內(nèi)聲明形式如下:friendtypeoperator@(參數(shù)表);類外函數(shù)實現(xiàn)形式如下:typeoperator@(參數(shù)表){//函數(shù)體}與成員函數(shù)不同,友元運算符函數(shù)是不屬于任何類對象的,它沒有this指針。若重載的是雙目運算符,則參數(shù)表中需要兩個操作數(shù);若重載的是單目運算符,則參數(shù)表中需要一個操作數(shù)。12/23/2024222.雙目運算符重載當(dāng)用友元函數(shù)重載雙目運算符時,兩個操作數(shù)都要傳遞給運算符函數(shù),調(diào)用時可采用以下兩種方式:A@B; //隱式調(diào)用operator@(A,B); //顯式調(diào)用雙目友元運算符函數(shù)operator@所需要的兩個操作數(shù)都在參數(shù)表中由對象A和B顯式調(diào)用。下面我們給出一個例子。例11.4雙目運算符重載為友元函數(shù)代碼如下:12/23/202423例11.4雙目運算符重載為友元函數(shù)代碼如下:#include<iostream.h>classcomplex{private:doublereal;doubleimag;public:complex(doubler=0.0,doublei=0.0);friendcomplexoperator+(complexa,complexb); //重載“+”運算符為復(fù)數(shù)類友元函數(shù)

friendcomplexoperator-(complexa,complexb); //重載“-”運算符為復(fù)數(shù)類友元函數(shù)

voiddisplay();};complex::complex(doubler,doublei){real=r;imag=i;}complexoperator+(complexa,complexb){complextemp;temp.real=a.real+b.real;temp.imag=a.imag+b.imag;returntemp;}12/23/202424complexoperator-(complexa,complexb){complextemp;temp.real=a.real-b.real;temp.imag=a.imag-b.imag;

returntemp;}voidcomplex::display(){cout<<real;if(imag>0)cout<<"+";if(imag!=0)cout<<imag<<"i\n";}main(){complexA1(1.9,6.9),A2(0.9,0.9),A3,A4A3=A1+A2;A4=A1-A2;A1.display();

A2.display();A3.display();A4.display();}程序運行結(jié)果如下:1.9+6.9i0.9+0.9i2.8+7.8i1+6i12/23/2024253.單目運算符重載當(dāng)用友元函數(shù)重載單目運算符時,需要一個顯式的操作數(shù),調(diào)用時可采用以下兩種方式:@A;//隱式調(diào)用operator@(A);//顯式調(diào)用下面我們給出一個應(yīng)用例子。12/23/202426例11.5單目運算符重載為友元函數(shù),代碼如下:#include<iostream.h>classMyClass{intx,y;public:MyClass(inti=0,intj=0);voidprint();friendMyClassoperator++(MyClass&op); //定義單目運算符“++”函數(shù)為友元函數(shù)};MyClass::MyClass(inti,intj){x=i;y=j;}voidMyClass::print(){cout<<"x:"<<x<<",y:"<<y<<endl;}MyClassoperator++(MyClass&op) //前置++{++op.x;++op.y;returnop;}12/23/202427main(){MyClassob(10,20);ob.print();++ob; //隱式調(diào)用;前置++ob.print();operator++(ob); //顯式調(diào)用;前置++ob.print();}12/23/202428需要注意的是,使用友元函數(shù)重載“++”“--”這樣的運算符,可能會出現(xiàn)一些問題。我們回顧一下例11.3用成員函數(shù)重載“++”的成員運算符函數(shù)的情況。MyClassMyClass::operator++() //前置++{++x;++y;return*this;}由于所有的成員都有一個this指針,this指針指向該函數(shù)所屬類對象的指針,因此對私有數(shù)據(jù)x和y的任何修改都將影響實際調(diào)用運算符函數(shù)的對象。因此例11.3的執(zhí)行是完全正確的。但是如果像下面那樣用友元函數(shù)按以下方式改寫例11.5的運算符函數(shù),將不會改變main()函數(shù)中對象ob的值。12/23/202429MyClassoperator++(MyClassop) //前置++{++op.x;++op.y;returnop;}為了解決以上問題,使用友元函數(shù)重載單目運算符“++”和“--”時,應(yīng)采用引用參數(shù)傳遞操作數(shù),如例11.5,這樣形參的任何改變都影響實參,從而保持了兩個運算符的原義。12/23/20243011.3.4“++”和“--”的重載我們知道,運算符“++”和“--”作前綴和后綴是有區(qū)別的。但是在C++v2.1之前的版本重載“++”或“--”時,不能顯示區(qū)分是前綴方式還是后綴方式;在C++v2.1及以后的版本中,編輯器可以通過在運算符函數(shù)表中是否插入關(guān)鍵字int來區(qū)分這兩種方式。對于前綴方式?++ob,可以使用運算符函數(shù)重載為:ob.operator++(); //成員函數(shù)重載;前置++operator++(X&ob); //友元函數(shù)重載,X為要操作的類型;前置++對于后綴方式ob++,可以使用運算符函數(shù)重載為:ob.operator++(int); //成員函數(shù)重載;后置++operator++(X&ob,int); //友元函數(shù)重載;后置++調(diào)用時,參數(shù)int一般被傳遞給值0。我們看如下例子。12/23/202431例11.6運算符“++”和“--”作前綴和后綴重載。#include<iostream.h>#include<iomanip.h>classMyClass{

intx1,x2,x3;public:voidinit(inta1,inta2,inta3);voidprint();MyClassoperator++(); //成員函數(shù)重載"++"前綴方式原型

MyClassoperator++(int); //成員函數(shù)重載"++"后綴方式原型

friendMyClassoperator--(MyClass&); //友元函數(shù)重載"--"前綴方式原型

friendMyClassoperator--(MyClass&,int); //友元函數(shù)重載"--"后綴方式原型};12/23/202432voidMyClass::init(inta1,inta2,inta3){x1=a1;x2=a2;x3=a3;}voidMyClass::print(){cout<<"x1:"<<x1<<"x2:"<<x2<<"x3:"<<x3<<endl;}MyClassMyClass::operator++(){++x1;++x2;++x3;return*this;}MyClassMyClass::operator++(int){MyClassold;old=*this;x1++;x2++;x3++;returnold;}MyClassoperator--(MyClass&op){--op.x1;--op.x2;--op.x3;returnop;}MyClassoperator--(MyClass&op,int){MyClassold;old=op;op.x1--;op.x2--;op.x3--;returnold;}12/23/202433main(){MyClassobj1,obj2,obj3,obj4;

obj1.init(4,2,5);obj2.init(2,5,9);obj3.init(8,3,8);obj4.init(3,6,7);++obj1;obj2++;--obj3;obj4--;obj1.print();obj2.print();obj3.print();

obj4.print();cout<<"--------------------"<<endl;obj1.operator++();obj2.operator++(0);operator--(obj3);operator--(obj4,0);obj1.print();obj2.print();obj3.print();obj4.print();}12/23/202434程序運行結(jié)果如下:x1:5x2:3x3:6x1:3x2:6x3:10x1:7x2:2x3:7x1:2x2:5x3:6--------------------x1:6x2:4x3:7x1:4x2:7x3:11x1:6x2:1x3:6x1:1x2:4x3:512/23/202435

案例三虛函數(shù)使用1.問題描述設(shè)計玩具和玩具車,玩具車由玩具派生,設(shè)計輸出函數(shù)來實現(xiàn)玩具車信息輸出。2.問題分析注意掌握虛函數(shù)的定義方法、派生類同名成員函數(shù)的二義性避免方法、聲明成虛函數(shù)后同名函數(shù)覆蓋。12/23/2024363.?C++?代碼/*本程序分別定義一個toy基類和toy類的派生類toycar;主函數(shù)定義一個基類指針分別指向基類和派生類的對象,通過該指針調(diào)用兩個類中的虛函數(shù)和普通函數(shù),呈現(xiàn)不同結(jié)果*/#include<iostream.h>classtoy{public:virtualvoidshowname() //定義虛函數(shù)showname(){cout<<"Thisisatoy.\n";}voidAskingprice() //定義Askingprice(){cout<<"Howmuchisthistoy?\n";}};classtoycar:publictoy{public:voidshowname() //重新定義虛函數(shù)showname(){cout<<"Thisisatoycar.\n";}voidAskingprice() //定義Askingprice(){cout<<"Howmuchisthistoycar?\n";}};12/23/202437voidmain(){toyob1,*op1;toycarob2,*op2;op1=&ob1; //基類指針指向基類對象

op1->showname(); //調(diào)用基類指針指向的基類對象的虛成員函數(shù)

op1->Askingprice(); //調(diào)用基類指針指向的基類對象的成員函數(shù)

op1=&ob2; //基類指針指向派生類對象

op1->showname(); //調(diào)用基類指針指向的派生類對象的虛成員函數(shù),注意輸出

op1->Askingprice(); //調(diào)用基類指針指向的派生類對象的成員函數(shù),比較和上面輸出不同

op2=&ob2; //派生類指針指向派生類對象

op2->showname(); //調(diào)用派生類指針指向的派生類對象的虛成員函數(shù),注意輸出

op2->Askingprice();//調(diào)用派生類指針指向的派生類對象的成員函數(shù),比較和上面輸出不同}12/23/2024384.程序運行結(jié)果Thisisatoy.Howmuchisthistoy?Thisisatoycar.Howmuchisthistoy?Thisisatoycar.Howmuchisthistoycar?12/23/20243911.4虛函數(shù)

虛函數(shù)是重載的另一種表現(xiàn)形式。虛函數(shù)允許函數(shù)調(diào)用與函數(shù)體之間的聯(lián)系在程序運行時才建立,也就是在運行時才決定如何動作,即所謂的動態(tài)聯(lián)編。12/23/20244011.4.1引入派生類后的對象指針

引入派生類后,由于派生類是由基類派生出來的,因此指向基類和派生類的指針也是相關(guān)的。其特點如下:(1)聲明為指向基類對象的指針可以指向它的公有派生的對象,但不允許指向它的私有派生對象。(2)允許將一個聲明為指向基類的指針指向其公有派生類的對象,但是不能將一個聲明為指向派生類對象的指針指向其基類的對象。(3)聲明為指向基類對象的指針,當(dāng)其指向其公有派生類對象時,只能用它來直接訪問派生類中從基類繼承來的成員,而不能直接訪問公有派生類中定義的成員。12/23/202441例11.7基類指針指向派生類。#include<iostream.h>classBase{inta,b;public:Base(intx,inty){a=x;b=y;}voiddisplay(){cout<<"Base--------\n";cout<<a<<""<<b<<endl;}};12/23/202442classDerived:publicBase{intc;public:Derived(intx,inty,intz):Base(x,y){c=z;}voiddisplay(){cout<<"Derived---------\n";cout<<c<<endl;}};voidmain(){Baseob1(10,20),*op;Derivedob2(30,40,50);

op=&ob1;op->display();op=&ob2;op->display();}12/23/202443程序運行結(jié)果如下:Base--------1020Base--------3040從程序運行的結(jié)果可以看出,雖然執(zhí)行了語句op=&ob2后,指針op已經(jīng)指向了對象ob2,但是它所調(diào)用的函數(shù)仍然是其基類對象的display(),顯然這不是我們所希望的,我們希望這時調(diào)用的函數(shù)是Derived的display()。為了達(dá)到這個目的,我們可以將函數(shù)display()聲明為虛函數(shù)。12/23/20244411.4.2虛函數(shù)的定義及使用

在例11.7中,使用對象指針的目的是為了表達(dá)一種動態(tài)多態(tài)性,即當(dāng)時針指向不同對象時執(zhí)行不同的操作,但該例中并沒有起到這種作用。要想實現(xiàn)這種動態(tài)多態(tài)性,我們引入虛函數(shù)的概念。虛函數(shù)就是在基類中被關(guān)鍵字virtual說明,并在派生類中重新定義的函數(shù)。在派生類中重新定義時,其函數(shù)原型,包括返回類型、函數(shù)名、參數(shù)個數(shù)與參數(shù)類型的順序,都必須與基類中的原型完全相同。虛函數(shù)這種機(jī)制是在派生類中會將繼承自基類的同名虛函數(shù)進(jìn)行覆蓋,原有繼承的函數(shù)將不復(fù)存在,以嶄新的形式覆蓋原函數(shù);如果不是虛函數(shù),將不會覆蓋,產(chǎn)生新的同名函數(shù),在派生類中引用新的同名函數(shù),會隱藏基類的同名函數(shù),在派生類中還可以調(diào)用基類隱藏的同名函數(shù),只是要在該函數(shù)前面加基類的域標(biāo)識符。虛函數(shù)只能在類中使用,屬于類的成員函數(shù),但不能是構(gòu)造函數(shù)。另外,虛函數(shù)屬于動態(tài)聯(lián)編。12/23/202445我們使用虛函數(shù)改寫例11.7。例11.8使用虛函數(shù)實現(xiàn)動態(tài)多態(tài)性。#include<iostream.h>classBase{inta,b;public:Base(intx,inty){a=x;b=y;}virtualvoiddisplay()//定義虛函數(shù)display(){cout<<"Base--------\n";cout<<a<<""<<b<<endl;}};classDerived:publicBase{

intc;public:Derived(intx,inty,intz):Base(x,y){c=z;}voiddisplay()//重新定義虛函數(shù)display(){cout<<"Derived---------\n";cout<<c<<endl;}};12/23/202446voidmain(){Baseob1(10,20),*op;Derivedob2(30,40,50);

op=&ob1;op->display();op=&ob2;op->display();}12/23/202447程序運行結(jié)果如下:Base--------1020Derived---------50從程序運行的結(jié)果可以看出,執(zhí)行了語句“op=&ob2;”后,基類指針op已經(jīng)指向了對象ob2,它所調(diào)用的函數(shù)仍然是Derived的display(),達(dá)到了我們的要求。12/23/202448說明:(1)在基類中,用關(guān)鍵字virtual可以將其public或protected部分的成員函數(shù)聲明為虛函數(shù)。在派生類對基類中聲明的虛函數(shù)進(jìn)行重新定義時,關(guān)鍵字virtual可以寫也可以不寫。(2)虛函數(shù)被重新定義時,其函數(shù)的原型與基類中的函數(shù)原型必須完全相同。(3)一個虛函數(shù)無論被公有繼承多少次,它仍然保持虛函數(shù)的特性。(4)虛函數(shù)必須是其所在類的成員函數(shù),而不能是靜態(tài)成員函數(shù),因為虛函數(shù)調(diào)用要靠特定的對象來決定該激活哪個函數(shù)。但是虛函數(shù)可以在另一個類中被聲明為友元函數(shù)。12/23/20244911.4.3虛析構(gòu)函數(shù)構(gòu)造函數(shù)不能是虛函數(shù),但析構(gòu)函數(shù)可以是虛函數(shù)。我們看一個例子。例11.9析構(gòu)函數(shù)定義不當(dāng)造成內(nèi)存泄露。#include"iostream.h"classA{public:int*a;A(){a=new(int);}virtualvoidfunc1(){}~A(){deletea;cout<<"deletea"<<endl;}};classB:publicA{public:int*b;B(){b=new(int);}virtualvoidfunc1(){}~B(){deleteb;cout<<"deleteb"<<endl;}};voidmain(){A*pb=newB();deletepb;}12/23/202450程序運行結(jié)果如下:deletea在main函數(shù)中,創(chuàng)建了一個B類對象。當(dāng)B對象創(chuàng)建時,調(diào)用的是B類的構(gòu)造函數(shù)。但是,當(dāng)對象析構(gòu)時,卻調(diào)用的是A類的析構(gòu)函數(shù),B類的析構(gòu)函數(shù)沒有被調(diào)用,因而發(fā)生了內(nèi)存泄漏,這是我們不希望看到的。造成這種問題的原因是:當(dāng)A類指針指向的內(nèi)存單元(即B類對象的數(shù)據(jù))被釋放時,編譯器看到指針類型是A類的,所以調(diào)用A類的析構(gòu)函數(shù)。其實,這個時候我們需要調(diào)用指針?biāo)赶虻膶ο箢愋偷奈鰳?gòu)函數(shù)是B類析構(gòu)函數(shù)。虛函數(shù)能夠滿足這個要求。因此,這里我們要使用虛析構(gòu)函數(shù)來解決上面遇到的問題。12/23/202451例11.10虛析構(gòu)函數(shù)的使用。#include"iostream.h"classA{public:int*a;A(){a=new(int);}virtualvoidfunc1(){}virtual~A(){deletea;cout<<"deletea"<<endl;}};classB:publicA{public:int*b;B(){b=new(int);}virtualvoidfunc1(){}virtual~B(){deleteb;cout<<"deleteb"<<endl;}};voidmain(){A*pb=newB();deletepb;}12/23/202452程序運行后得到我們所希望的結(jié)果:deletebdeletea從以上程序中我們還可以看到:虛析構(gòu)函數(shù)的工作過程與普通虛函數(shù)不同,普通虛函數(shù)只是調(diào)用相應(yīng)層上的函數(shù),而虛析構(gòu)函數(shù)是先調(diào)用相應(yīng)層上的析構(gòu)函數(shù),然后逐層向上調(diào)用基類的析構(gòu)函數(shù)。12/23/202453案例四純虛函數(shù)與抽象類

1.問題描述定義一個抽象基類shape,由它派生出兩個派生類:ciecle(圓形)、triangle(三角形),用一個函數(shù)displayarea分別輸出以上兩個圖形的面積。要求用基類的指針,分別使它指向一個派生類的對象。2.問題分析注意掌握純虛函數(shù)的定義方法和使用特點。12/23/2024543.?C++?代碼/*本程序分別定義一個shape基類及其純虛函數(shù)displayarea和shape類的兩個派生類circle與triangle,并分別在兩個類中實現(xiàn)displayarea;主函數(shù)定義一個基類指針分別指向基類和派生類的對象,通過該指針調(diào)用兩個派生類對象中的虛函數(shù)*/#include<iostream.h>classshape{public:virtualvoiddisplayarea()=0;//定義純虛函數(shù)displayarea()};classcircle:publicshape{private:floatR;public:circle(floatr){R=r;}voiddisplayarea() //定義實現(xiàn)voiddisplayarea(){cout<<"圓面積為:"<<3.14*R*R<<endl;}};12/23/202455classtriangle:publicshape{private:floatH,W;public:triangle(floath,floatw){H=h;W=w;}voiddisplayarea() //定義實現(xiàn)voiddisplayarea(){cout<<"三角形面積為:"<<0.5*H*W<<endl;}};voidmain(){shape*pshape;circlecir(10);pshape=○

pshape->displayarea();triangletri(20,10);pshape=&tri;pshape->displayarea();}4.程序運行結(jié)果圓面積為:314三角形面積為:10012/23/20245611.5純虛函數(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)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論