c++面向?qū)ο蟪绦蛟O(shè)計(jì)第5版_第1頁(yè)
已閱讀1頁(yè),還剩54頁(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)介

1、C++面向?qū)ο蟪绦蛟O(shè)計(jì)(第5版),浙江工業(yè)大學(xué) 軟件學(xué)院,,國(guó)外經(jīng)典教材.計(jì)算機(jī)科學(xué)與技術(shù) (美) Walter Savitch,第5章 多態(tài)性與虛函數(shù),5.1 多態(tài)性的概念5.2 一個(gè)典型的例子5.3 虛函數(shù)5.4 純虛函數(shù)與抽象類(lèi),5.1 多態(tài)性的概念,在C++中,基類(lèi)指針或引用可以直接引用其任何派生子 類(lèi),而無(wú)需程序員介入——這種“用基類(lèi)的指針或引用操縱多個(gè)類(lèi)型”的能力稱(chēng)為多態(tài)

2、(polymorphism)。多態(tài)性(polymorphism)是面向?qū)ο蟪绦蛟O(shè) 計(jì)的一個(gè)重要特征。利用多態(tài)性可以設(shè)計(jì)和實(shí)現(xiàn)一個(gè)易于擴(kuò)展的系統(tǒng)。,,例如,已知基類(lèi)Camera派生出兩個(gè)子類(lèi)OrthographicCamera和PerspectiveCamera:已知函數(shù): void lookAt( const Camera *pcamera );,Camera,Perspectiv

3、eCamera,OrthographicCamera,,每次lookAt()函數(shù)調(diào)用時(shí)都會(huì)傳入一個(gè)Camera子類(lèi)對(duì)象的地址,編譯器會(huì)自動(dòng)地把它轉(zhuǎn)換成適當(dāng)?shù)幕?lèi)指針。例如:OrthographicCamera ocam;lookAt( &ocam ); //ok:自動(dòng)轉(zhuǎn)換成 Camera*PerspectiveCamera *pcam = new PerspectiveCamera;lookAt( pcam

4、); //ok:自動(dòng)被轉(zhuǎn)換成Camera*lookAt()的實(shí)現(xiàn)被屏蔽在應(yīng)用程序的實(shí)際Camera子類(lèi)之外,如果以后增加或刪除一個(gè)子類(lèi),無(wú)需改變lookAt().子類(lèi)多態(tài)性使得我們?cè)诰帉?xiě)應(yīng)用程序的核心時(shí),可以不用考慮將來(lái)需要維護(hù)的單個(gè)類(lèi)型。,,從系統(tǒng)實(shí)現(xiàn)的角度看,多態(tài)性分為兩類(lèi): 靜態(tài)多態(tài)性和動(dòng)態(tài)多態(tài)性。靜態(tài)多態(tài)性是通過(guò)函數(shù)的重載實(shí)現(xiàn)的。利用基類(lèi)指針和引用,對(duì)抽象基類(lèi)的公有接口進(jìn)行編程。在運(yùn)行時(shí)刻,真正要引用的類(lèi)型被解析出來(lái)

5、,并且調(diào)用適當(dāng)?shù)墓薪涌趯?shí)例。多態(tài)性是“一個(gè)接口,多種方法”。在運(yùn)行時(shí)刻解析出被調(diào)用的函數(shù),這個(gè)解析過(guò)程被稱(chēng)為動(dòng)態(tài)綁定(dynamic binding)。在C++中,通過(guò)虛擬函數(shù)機(jī)制來(lái)支持動(dòng)態(tài)綁定。通過(guò)繼承和動(dòng)態(tài)綁定,子類(lèi)型多態(tài)性為面向?qū)ο蟮某绦蛟O(shè)計(jì)提供了基礎(chǔ)。,5.2 一個(gè)典型的例子,下面是一個(gè)承上啟下的例子。一方面它是有關(guān)繼承和運(yùn)算符重載內(nèi)容的綜合應(yīng)用的例子,通過(guò)這個(gè)例子可以進(jìn)一步融會(huì)貫通前面所學(xué)的內(nèi)容,另一方面又是作為討論

6、多態(tài)性的一個(gè)基礎(chǔ)用例。例5.1: 先建立一個(gè)Point(點(diǎn))類(lèi),包含數(shù)據(jù)成員x,y(坐標(biāo)點(diǎn))。以它為基類(lèi),派生出一個(gè)Circle(圓)類(lèi),增加數(shù)據(jù)成員r(半徑),再以Circle類(lèi)為直接基類(lèi),派生出一個(gè)Cylinder(圓柱體)類(lèi),再增加數(shù)據(jù)成員h(高)。要求編寫(xiě)程序,重載運(yùn)算符“>”,使之能用于輸出以上類(lèi)對(duì)象。,,聲明基類(lèi)Point類(lèi)可寫(xiě)出聲明基類(lèi)Point的部分如下:#include //聲明類(lèi)Pointcla

7、ss Point{ public: Point(float x=0,float y=0);//有默認(rèn)參數(shù)的構(gòu)造函數(shù) void setPoint(float,float); //設(shè)置坐標(biāo)值float getX( ) const {return x;} //讀x坐標(biāo) float getY( ) const {return y;}

8、 //讀y坐標(biāo)friend ostream & operator<<(ostream &,const Point &);//重載運(yùn)算符“<<” protected: //受保護(hù)成員 float x,y;};,,//下面定義Point類(lèi)的成員函數(shù)//Point的構(gòu)造函數(shù)Point::Poin

9、t(float a,float b) //對(duì)x,y初始化{x=a;y=b;}//設(shè)置x和y的坐標(biāo)值void Point::setPoint(float a,float b) //為x,y賦新值{x=a;y=b;}//重載運(yùn)算符“<<”,使之能輸出點(diǎn)的坐標(biāo)ostream & operator<<(ostream &output,cons

10、t Point &p){output<<″[″<<p.x<<″,″<<p.y<<″]″<<endl; return output;},,編寫(xiě)測(cè)試程序main函數(shù):int main( ){ Point p(3.5,5.4); //建立Point類(lèi)對(duì)象p cout<<″x=″<<p.getX( )&

11、lt;<″,y=″<<p.getY( )<<endl; //輸出p的坐標(biāo)值 p.setPoint(8.5,5.8); //重新設(shè)置p的坐標(biāo)值 cout<<″p(new):″<<p<<endl; //用重載運(yùn)算符“<<”輸出p點(diǎn)坐標(biāo)}程序編譯通過(guò),運(yùn)行結(jié)果

12、為 x=3.5,y=5.4 p(new):[8.5,5.8]測(cè)試程序檢查了基類(lèi)中各函數(shù)的功能,以及運(yùn)算符重載的作用,證明程序是正確的。,,(2) 聲明派生類(lèi)Circleclass Circle : public Point //circle是Point類(lèi)的公用派生類(lèi){ public: Circle(float x=0,float y=0,float r=0); //構(gòu)造函數(shù)

13、 void setRadius(float); //設(shè)置半徑值 float getRadius( ) const; //讀取半徑值float area ( ) const; //計(jì)算圓面積 friend ostream &operator<<(ostream &,const Cir

14、cle &); //重載運(yùn)算符“<<” private: float radius;};,,//定義構(gòu)造函數(shù),對(duì)圓心坐標(biāo)和半徑初始化Circle::Circle(float a,float b,float r):Point(a,b),radius(r){ }//設(shè)置半徑值void Circle::setRadius(float r){radius=r;}

15、//讀取半徑值float Circle::getRadius( ) const {return radius;}//計(jì)算圓面積float Circle::area( ) const{return 3.14159*radius*radius;}//重載運(yùn)算符“<<”,使之按規(guī)定的形式輸出圓的信息ostream &operator<<(ostream &output,const C

16、ircle &c){ output<<″Center=[″<<c.x<<″,″<<c.y<<″],r=″<<c.radius<<″,area =″<<c.area( )<<endl; return output;},,測(cè)試Circle類(lèi)定義的主函數(shù):int main( ){

17、 Circle c(3.5,5.4,5.2);//建立Circle類(lèi)對(duì)象c,并給定圓心坐標(biāo)和半徑 cout<<″original circle:\\nx=″<<c.getX()<<″, y=″<<c.getY() <<″, r=″<<c.getRadius( ) <<″, area=″<<c.area(

18、)<<endl; //輸出圓心坐標(biāo)、半徑和面積 c.setRadius(7.5); //設(shè)置半徑值 c.setPoint(5,5); //設(shè)置圓心坐標(biāo)值x,y cout<<″new circle:\\n″<<c; //用重載運(yùn)算符“<<”輸出圓對(duì)象

19、的信息 Point &pRef=c; //pRef是Point類(lèi)的引用變量,被c初始化 cout<<″pRef:″<<pRef; //輸出pRef的信息 return 0;}程序編譯通過(guò),運(yùn)行結(jié)果為:original circle:x=3.5, y=5.4, r=5.2, area=84.94

20、85new circle: Center=[5,5], r=7.5, area=175.714pRef:[5,5],,(3) 聲明Circle的派生類(lèi)Cylinderclass Cylinder:public Circle// Cylinder是Circle的公用派生類(lèi){ public: Cylinder (float x=0,float y=0,flo

21、at r=0,float h=0);void setHeight(float); //設(shè)置圓柱高 float getHeight( ) const; //讀取圓柱高 float area( ) const; //計(jì)算圓表面積 float volume( ) const;

22、 //計(jì)算圓柱體積 friend ostream& operator<<(ostream&,const Cylinder&); protected: float height; //圓柱高};,,//定義構(gòu)造函數(shù)Cylinder::Cylinder(float a,float

23、 b,float r,float h) : Circle(a,b,r),height(h){}//設(shè)置圓柱高void Cylinder::setHeight(float h){height=h;}//讀取圓柱高float Cylinder::getHeight( ) const {return height;}//計(jì)算圓表面積float Cylinder::area( ) const{ return 2*C

24、ircle::area( )+2*3.14159*radius*height;}//計(jì)算圓柱體積float Cylinder::volume() const{return Circle::area()*height;}//重載運(yùn)算符“<<”ostream &operator<<(ostream &output,const Cylinder& cy){output<&l

25、t;″Center=[″<<cy.x<<″,″<<cy.y<<″],r=″<<cy.radius<<″,h=″<<cy.height <<″\\narea=″<<cy.area( )<<″, volume=″<<cy.volume( )<<endl; return output;

26、},,寫(xiě)出下面的測(cè)試主函數(shù): int main( ){Cylinder cy1(3.5,5.4,5.2,10);//定義Cylinder類(lèi)對(duì)象cy1 cout<<″\\noriginal cylinder:\\nx=″<<cy1.getX( ) <<″,y=″<<cy1.getY( )<<″, r=″ <<

27、cy1.getRadius( )<<″,h=″<<cy1.getHeight( ) <<″\\narea=″<<cy1.area() <<″,volume=″<<cy1.volume()<<endl;//用系統(tǒng)定義的運(yùn)算符“<<”輸出cy1的數(shù)據(jù) cy1.setHeight(15);

28、 //設(shè)置圓柱高 cy1.setRadius(7.5); //設(shè)置圓半徑 cy1.setPoint(5,5); //設(shè)置圓心坐標(biāo)值x,y cout<<″\\nnew cylinder:\\n″<<cy1; //用重載運(yùn)算符“<<”輸出cy1的數(shù)據(jù) Point &pRef=cy1;

29、 //pRef是Point類(lèi)對(duì)象的引用變量cout<<″\\npRef as a Point:″<<pRef; //pRef作為一個(gè)“點(diǎn)”輸出 Circle &cRef=cy1; //cRef是Circle類(lèi)對(duì)象的引用變量 cout<<″\\ncRef as a Circle:″<

30、<cRef; //cRef作為一個(gè)“圓”輸出 return 0;},,運(yùn)行結(jié)果如下:original cylinder: (輸出cy1的初始值)x=3.5, y=5.4, r=5.2, h=10 (圓心坐標(biāo)x,y。半徑r,高h(yuǎn))area=495.523, volume=849.485 (圓柱表面積area和體積volume

31、)new cylinder: (輸出cy1的新值)Center=[5,5], r=7.5, h=15 (以[5,5]形式輸出圓心坐標(biāo))area=1050.29, volume=2550.72 (圓柱表面積area和體積volume)pRef as a Point:[5,5] (pRef作為

32、一個(gè)“點(diǎn)”輸出)cRef as a Circle: Center=[5,5], r=7.5, area=175.714(cRef作為一個(gè)“圓”輸出)在本例中存在靜態(tài)多態(tài)性,這是運(yùn)算符重載引起的??梢钥吹?,在編譯時(shí)編譯系統(tǒng)即可以判定應(yīng)調(diào)用哪個(gè)重載運(yùn)算符函數(shù)。,5.3 虛函數(shù) 5.3.1 虛函數(shù)的作用,在類(lèi)的繼承層次結(jié)構(gòu)中,在不同的層次中可以出現(xiàn)名字相同、參數(shù)個(gè)數(shù)和類(lèi)型都相同而功能不同的函數(shù)。編譯系統(tǒng)按照同名覆蓋的原則決定調(diào)用的

33、對(duì)象。例如:例5.1程序中cy1.area( )調(diào)用的是派生類(lèi)Cylinder中的成員函數(shù)area。如果想調(diào)用cy1中的直接基類(lèi)Circle的area函數(shù),改如何調(diào)用呢?應(yīng)當(dāng)表示為: cy1.Circle::area( )。用這種方法來(lái)區(qū)分兩個(gè)同名的函數(shù)很不方便。,,能否用同一個(gè)調(diào)用形式,既能調(diào)用派生類(lèi)又能調(diào)用基類(lèi)的同名函數(shù)?可以在程序中通過(guò)指針調(diào)用基類(lèi)和派生類(lèi)的同名函數(shù),根據(jù)指針實(shí)際指向的對(duì)象來(lái)調(diào)用對(duì)應(yīng)的函數(shù)。例如,語(yǔ)句

34、pt->display( ); 可以調(diào)用不同派生層次中的display函數(shù),只需在調(diào)用前給指針變量pt賦以不同的值(使之指向不同的類(lèi)對(duì)象)即可。C++通過(guò)虛函數(shù)來(lái)解決這個(gè)問(wèn)題。虛函數(shù)的作用是允許在派生類(lèi)中重新定義與基類(lèi)同名的函數(shù),并且可以通過(guò)基類(lèi)指針或引用來(lái)訪問(wèn)基類(lèi)和派生類(lèi)中的同名函數(shù)。我們來(lái)分析例5.2。這個(gè)例子開(kāi)始時(shí)沒(méi)有使用虛函數(shù),然后再討論使用虛函數(shù)的情況。,,例5.2 基類(lèi)Student與派生類(lèi)Graduate有同

35、名函數(shù)。#include #include using namespace std;//聲明基類(lèi)Studentclass Student{ public: Student(int, string,float); //聲明構(gòu)造函數(shù) void display( ); /聲明輸出函數(shù) protected:

36、 //受保護(hù)成員,派生類(lèi)可以訪問(wèn) int num; string name; float score; };,,//Student類(lèi)成員函數(shù)的實(shí)現(xiàn)Student::Student(int n, string nam,float s) //定義構(gòu)造函數(shù) {num=n;name=nam;score=s;}void Student::d

37、isplay( ) //定義輸出函數(shù){cout<<″num:″<<num<<″\\nname:″<<name<<″\\nscore:″<<score<<″\\n\\n″;},,//聲明公用派生類(lèi)Graduateclass Graduate:public Student{p

38、ublic: Graduate(int, string, float, float); //聲明構(gòu)造函數(shù) void display( ); //聲明輸出函數(shù)private: float pay;};// Graduate類(lèi)成員函數(shù)的實(shí)現(xiàn)Graduate::Graduate(int n, stri

39、ng nam,float s,float p):Student(n,nam,s),pay(p){ }void Graduate::display( ) //定義輸出函數(shù){cout<<″num:″<<num<<″\\nname:″<<name<<″\\nscore:″<<score<<″

40、\\npay=″<<pay<<endl;},,//主函數(shù)int main(){Student stud1(1001, “Li”, 87.5);Graduate grad1(2001, “Wang”, 98.5, 563.5); Student *pt = &stud1;pt->display();pt = &grad1;pt->displ

41、ay();return 0;},,運(yùn)行結(jié)果如下,請(qǐng)仔細(xì)分析。num:1001name:Liscore:87.5num:2001 name:wangscore:98.5,,對(duì)程序作一點(diǎn)修改,在Student類(lèi)中聲明display函數(shù)時(shí),在最左面加一個(gè)關(guān)鍵字virtual,即 virtual void display( );再編譯和運(yùn)行程

42、序,請(qǐng)注意分析運(yùn)行結(jié)果: num:1001name:Liscore:87.5num:2001 name:wangscore:98.5pay=1200,5.3.2 C++中的虛函數(shù),假定我們要為汽車(chē)零件商店設(shè)計(jì)一個(gè)記帳程序。希望該程序非常靈活,能夠兼容以后出現(xiàn)的新銷(xiāo)售情況。為了適應(yīng)這種情況,將計(jì)算賬單的函數(shù)設(shè)為虛函數(shù)。設(shè)計(jì)基類(lèi)Sale, Sale類(lèi)對(duì)應(yīng)于單件商品的簡(jiǎn)單銷(xiāo)售。所有類(lèi)型的銷(xiāo)售(如打折銷(xiāo)售)都是Sale類(lèi)

43、的派生類(lèi)。,,由虛函數(shù)實(shí)現(xiàn)的動(dòng)態(tài)多態(tài)性就是:基類(lèi)的指針或引用可以指向其任意派生類(lèi)的能力。虛函數(shù)的使用方法是: 在基類(lèi)用virtual聲明成員函數(shù)為虛函數(shù)。在派生類(lèi)中重新定義此函數(shù),要求函數(shù)名、函數(shù)類(lèi)型、函數(shù)參數(shù)個(gè)數(shù)和類(lèi)型全部與基類(lèi)的虛函數(shù)相同,并根據(jù)派生類(lèi)的需要重新定義函數(shù)體。定義一個(gè)指向基類(lèi)對(duì)象的指針變量,并使它指向同一類(lèi)繼承層次結(jié)構(gòu)中的某個(gè)對(duì)象。通過(guò)該指針變量調(diào)用此虛函數(shù),此時(shí)調(diào)用的就是指針變量指向的對(duì)象的同名函數(shù)。,,C

44、++規(guī)定,當(dāng)一個(gè)成員函數(shù)被聲明為虛函數(shù)后,其派生類(lèi)中的同名函數(shù)都自動(dòng)成為虛函數(shù)。在派生類(lèi)重新聲明該虛函數(shù)時(shí),可以加virtual,也可以不加,但習(xí)慣上一般在每一層聲明該函數(shù)時(shí)都加virtual,使程序更加清晰。如果在派生類(lèi)中沒(méi)有對(duì)基類(lèi)的虛函數(shù)重新定義,則派生類(lèi)簡(jiǎn)單地繼承其直接基類(lèi)的虛函數(shù)。特別提醒:有時(shí)在基類(lèi)中定義的非虛函數(shù)會(huì)在派生類(lèi)中被重新定義(如例5.1中的area函數(shù)),此時(shí)不存在多態(tài)性。,5.3.3 靜態(tài)關(guān)聯(lián)與動(dòng)態(tài)關(guān)聯(lián),計(jì)

45、算機(jī)系統(tǒng)如何選擇正確的調(diào)用對(duì)象,從而實(shí)現(xiàn)多態(tài)性?確定調(diào)用的具體對(duì)象的過(guò)程稱(chēng)為關(guān)聯(lián)(binding)。在這里是指把一個(gè)函數(shù)名與一個(gè)類(lèi)對(duì)象捆綁在一起,建立關(guān)聯(lián)。一般地說(shuō),關(guān)聯(lián)指把一個(gè)標(biāo)識(shí)符和一個(gè)存儲(chǔ)地址聯(lián)系起來(lái)。靜態(tài)關(guān)聯(lián)(static binding)和動(dòng)態(tài)關(guān)聯(lián)(dynamic binding)(晚期綁定),,在編譯時(shí)即可確定其調(diào)用的函數(shù)屬于哪一個(gè)類(lèi),其過(guò)程稱(chēng)為靜態(tài)關(guān)聯(lián)(static binding),又稱(chēng)為早期關(guān)聯(lián)(early bin

46、ding)。函數(shù)重載屬靜態(tài)關(guān)聯(lián)。 對(duì)于通過(guò)基類(lèi)指針調(diào)用虛函數(shù),編譯系統(tǒng)在編譯該行時(shí)是無(wú)法確定調(diào)用哪一個(gè)類(lèi)對(duì)象的虛函數(shù)的。在這樣的情況下,在運(yùn)行階段確定關(guān)聯(lián)關(guān)系。此過(guò)程稱(chēng)為動(dòng)態(tài)關(guān)聯(lián)(dynamic binding)。這種多態(tài)性是運(yùn)行階段的多態(tài)性。在運(yùn)行階段,指針可以先后指向不同的類(lèi)對(duì)象,從而調(diào)用同一類(lèi)族中不同類(lèi)的虛函數(shù)。由于動(dòng)態(tài)關(guān)聯(lián)是在編譯以后的運(yùn)行階段進(jìn)行的,因此也稱(chēng)為晚期關(guān)聯(lián)(late binding)。,5.3.4 在什么情況

47、下應(yīng)當(dāng)聲明虛函數(shù),使用虛函數(shù)時(shí),有兩點(diǎn)要注意: (1)虛函數(shù)只能作用于類(lèi)的繼承層次結(jié)構(gòu)中,即多態(tài)性只存在于類(lèi)繼承層次中。(2) 一個(gè)成員函數(shù)被聲明為虛函數(shù)后,在同一類(lèi)族中的類(lèi)就不能再定義一個(gè)非virtual的但與該虛函數(shù)具有相同的參數(shù)(包括個(gè)數(shù)和類(lèi)型)和函數(shù)返回值類(lèi)型的同名函數(shù)。,,根據(jù)什么考慮是否把一個(gè)成員函數(shù)聲明為虛函數(shù)呢?主要考慮以下幾點(diǎn): (1) 首先看成員函數(shù)所在的類(lèi)是否會(huì)作為基類(lèi)。然后看成員函數(shù)在類(lèi)的繼承后有無(wú)可能被更

48、改功能,如果希望更改其功能的,一般應(yīng)該將它聲明為虛函數(shù)。(2) 如果成員函數(shù)在類(lèi)被繼承后功能不需修改,或派生類(lèi)用不到該函數(shù),則不要把它聲明為虛函數(shù)。,,(3) 應(yīng)考慮對(duì)成員函數(shù)的調(diào)用是通過(guò)對(duì)象名還是通過(guò)基類(lèi)指針或引用去訪問(wèn),如果是通過(guò)基類(lèi)指針或引用去訪問(wèn)的,則應(yīng)當(dāng)聲明為虛函數(shù)。(4) 有時(shí)會(huì)在基類(lèi)定義具有空函數(shù)體的虛函數(shù)。它的作用只是定義了一個(gè)虛函數(shù)名(即公有接口),然后通過(guò)派生類(lèi)來(lái)添加具體功能。在5.4節(jié)中將詳細(xì)討論此問(wèn)題。(5

49、)編譯器和運(yùn)行環(huán)境需要為虛函數(shù)做較多的工作,所以,如果將不必要設(shè)置為虛函數(shù)的函數(shù)標(biāo)記為virtual,會(huì)影響程序的執(zhí)行效率。,5.3.5 虛析構(gòu)函數(shù),繼承機(jī)制下的析構(gòu)函數(shù)的行為如下:派生類(lèi)的析構(gòu)函數(shù)先被調(diào)用。完成之后,直接基類(lèi)的析構(gòu)函數(shù)被靜態(tài)調(diào)用。但是,如果用new運(yùn)算符建立了臨時(shí)對(duì)象,若基類(lèi)中有析構(gòu)函數(shù),并且定義了一個(gè)指向該基類(lèi)的指針變量。在程序用帶指針參數(shù)的delete運(yùn)算符撤銷(xiāo)對(duì)象時(shí),會(huì)發(fā)生一個(gè)情況: 系統(tǒng)會(huì)只執(zhí)行基類(lèi)的析構(gòu)函數(shù)

50、,而不執(zhí)行派生類(lèi)的析構(gòu)函數(shù)。,,例5.3 基類(lèi)中有非虛析構(gòu)函數(shù)時(shí)的執(zhí)行情況。(為簡(jiǎn)化程序,只列出最必要的部分。)#include using namespace std;class Point//定義基類(lèi)Point類(lèi){public: Point( ){ } //Point類(lèi)構(gòu)造函數(shù) ~Point(){cout<<

51、;″e(cuò)xecuting Point destructor″<<endl;}//Point類(lèi)析構(gòu)函數(shù)};class Circle:public Point //定義派生類(lèi)Circle類(lèi){public: Circle( ){ } //Circle類(lèi)構(gòu)造函

52、數(shù) ~Circle( ){cout<<″e(cuò)xecuting Circle destructor″<<endl;}//Circle類(lèi)析構(gòu)函數(shù) private: int radius;};int main( ){ Point *p=new Circle; //用new開(kāi)辟動(dòng)態(tài)存儲(chǔ)空間delete p;

53、 //用delete釋放動(dòng)態(tài)存儲(chǔ)空間return 0;},,運(yùn)行結(jié)果為:executing Point destructor表示只執(zhí)行了基類(lèi)Point的析構(gòu)函數(shù),而沒(méi)有執(zhí)行派生類(lèi)Circle的析構(gòu)函數(shù)。為了執(zhí)行派生類(lèi)Circle的析構(gòu)函數(shù),需要將基類(lèi)的析構(gòu)函數(shù)聲明為虛析構(gòu)函數(shù),即 virtual ~Point(){cout<<″e(cuò)xecuting Point

54、 destructor″<<endl;}程序其他部分不改動(dòng),再運(yùn)行程序,結(jié)果為executing Circle destructorexecuting Point destructor,,如果將基類(lèi)的析構(gòu)函數(shù)聲明為虛函數(shù)時(shí),由該基類(lèi)所派生的所有派生類(lèi)的析構(gòu)函數(shù)也都自動(dòng)成為虛函數(shù),即使派生類(lèi)的析構(gòu)函數(shù)與基類(lèi)的析構(gòu)函數(shù)名字不相同。最好把基類(lèi)的析構(gòu)函數(shù)聲明為虛函數(shù)。這將使所有派生類(lèi)的析構(gòu)函數(shù)自動(dòng)成為虛函數(shù)。

55、構(gòu)造函數(shù)不能聲明為虛函數(shù)。這是因?yàn)樵趫?zhí)行構(gòu)造函數(shù)時(shí)類(lèi)對(duì)象還未完成建立過(guò)程,當(dāng)然談不上函數(shù)與類(lèi)對(duì)象的綁定。,5.4 純虛函數(shù)與抽象類(lèi) 5.4.1 純虛函數(shù),C++語(yǔ)言提供了一種語(yǔ)法結(jié)構(gòu),通過(guò)它表明,一個(gè)虛擬函數(shù)只是提供了一個(gè)可被派生子類(lèi)型改寫(xiě)的接口,它本身并不能通過(guò)虛擬機(jī)制被調(diào)用——純虛擬函數(shù)例如在例5.1程序中,基類(lèi)Point可以提供求面積的純虛擬函數(shù):virtual float area( ) const =0;//純虛函

56、數(shù)其直接派生類(lèi)Circle和間接派生類(lèi)Cylinder可以改寫(xiě)這個(gè)area函數(shù),分別為求圓面積和求圓柱體表面積。,,純虛函數(shù)是在聲明虛函數(shù)時(shí)被“初始化”為0的函數(shù)。聲明純虛函數(shù)的一般形式是virtual 函數(shù)類(lèi)型 函數(shù)名 (參數(shù)表列) =0;注意: ①純虛函數(shù)沒(méi)有函數(shù)體;②最后面的“=0”并不表示函數(shù)返回值為0,它只起形式上的作用,告訴編譯系統(tǒng)“這是純虛函數(shù)”;③這是一個(gè)聲明語(yǔ)句,最后應(yīng)有分號(hào)。,5.4.2 抽象類(lèi)

57、,包含一個(gè)或多個(gè)純虛函數(shù)的類(lèi)稱(chēng)為抽象基類(lèi)(abstract class)。試圖創(chuàng)建一個(gè)抽象基類(lèi)的獨(dú)立類(lèi)對(duì)象會(huì)導(dǎo)致編譯錯(cuò)誤。抽象類(lèi)的作用是作為一個(gè)類(lèi)繼承層次結(jié)構(gòu)的共同基類(lèi),或者說(shuō),為一個(gè)類(lèi)繼承層次結(jié)構(gòu)提供一個(gè)公共接口。這種類(lèi)不用來(lái)定義對(duì)象,一般只作為一種被繼承的基本類(lèi)型。如果在派生類(lèi)中對(duì)基類(lèi)的所有純虛函數(shù)進(jìn)行了定義,那么這些函數(shù)就被賦予了功能,可以被調(diào)用。這時(shí)派生類(lèi)是可以用來(lái)定義對(duì)象的具體類(lèi)(concrete class);否則

58、派生類(lèi)仍然是抽象類(lèi),不能用來(lái)定義對(duì)象。我們可以定義指向抽象類(lèi)對(duì)象的指針變量,用此指針指向具體派生類(lèi)的對(duì)象,然后通過(guò)該指針調(diào)用虛函數(shù),實(shí)現(xiàn)多態(tài)性的操作。,5.4.3 應(yīng)用實(shí)例,例5.4 虛函數(shù)和抽象基類(lèi)的應(yīng)用。在例5.1介紹了以Point為基類(lèi)的點(diǎn)—圓—圓柱體類(lèi)的層次結(jié)構(gòu)?,F(xiàn)在對(duì)它進(jìn)行改寫(xiě),在程序中使用虛函數(shù)和抽象基類(lèi)。類(lèi)的層次結(jié)構(gòu)的頂層是抽象基類(lèi)Shape(形狀)。Point(點(diǎn)), Circle(圓), Cylinder(圓柱體

59、)都是Shape類(lèi)的直接派生類(lèi)和間接派生類(lèi)。程序如下:,第(1)部分#include using namespace std;//聲明抽象基類(lèi)Shapeclass Shape{ public: virtual float area( ) const {return 0.0;} virtual float volume() const {return 0.0;} virtual void shapeName(

60、) const =0; };,,第(2)部分//聲明Point類(lèi)class Point : public Shape//Point是Shape的公用派生類(lèi){ public: Point(float=0,float=0); void setPoint(float,float); float getX( ) const {return x;} float getY( ) c

61、onst {return y;} virtual void shapeName( ) const {cout<<″Point:″;} //對(duì)虛函數(shù)進(jìn)行再定義 friend ostream & operator<<(ostream &,const Point &); protected: f

62、loat x,y;};,,//定義Point類(lèi)成員函數(shù)Point::Point(float a,float b){x=a;y=b;}void Point::setPoint(float a,float b){x=a;y=b;}ostream & operator<<(ostream &output,const Point &p){output<<″[″<<

63、;p.x<<″,″<<p.y<<″]″;return output;},,第(3)部分//聲明Circle類(lèi)class Circle : public Point{ public: Circle(float x=0,float y=0,float r=0); void setRadius(float); float getRadius( ) const

64、; virtual float area( ) const; virtual void shapeName( ) const {cout<<″Circle:″;}//對(duì)虛函數(shù)進(jìn)行再定義 friend ostream &operator<<(ostream &,const Circle &); protected: float radius;};,,/

65、/聲明Circle類(lèi)成員函數(shù)Circle::Circle(float a,float b,float r):Point(a,b),radius(r){ }void Circle::setRadius(float r):radius(r){ }float Circle::getRadius( ) const {return radius;}float Circle::area( ) const {return 3.14159

66、*radius*radius;}ostream &operator<<(ostream &output,const Circle &c){output<<″[″<<c.x<<″,″<<c.y<<″], r=″<<c.radius; return output;},,第(4)部分//聲明Cylinder類(lèi)clas

67、s Cylinder : public Circle{ public: Cylinder (float x=0,float y=0,float r=0,float h=0); void setHeight(float); virtual float area( ) const; virtual float volume( ) const; virtual void shapeName( ) con

68、st {cout<<″Cylinder:″;}//對(duì)虛函數(shù)進(jìn)行再定義 friend ostream& operator<<(ostream&,const Cylinder&); protected: float height;};,,//定義Cylinder類(lèi)成員函數(shù)Cylinder::Cylinder(float a,float b,float r,float

69、 h) :Circle(a,b,r),height(h){ }void Cylinder::setHeight(float h){height=h;}float Cylinder::area( ) const{ return 2*Circle::area( )+2*3.14159*radius*height;}float Cylinder::volume( ) const{return Circle::area(

70、 )*height;}ostream &operator<<(ostream &output,const Cylinder& cy){output<<″[″<<cy.x<<″,″<<cy.y<<″], r=″<<cy.radius<<″, h=″<<cy.height;return outpu

71、t;},,第(5)部分//main函數(shù)int main( ){Point point(3.2,4.5);//建立Point類(lèi)對(duì)象point Circle circle(2.4,1.2,5.5); //建立Circle類(lèi)對(duì)象circle Cylinder cylinder(3.5,5.4,5.2,10.5); //建立Cylinder類(lèi)對(duì)象cylinder poi

72、nt.shapeName(); //靜態(tài)關(guān)聯(lián) coutshapeName( ); //動(dòng)態(tài)關(guān)聯(lián) coutarea( ) volume()<<″\\n\\n″; pt=&circle; //指針指向Circle類(lèi)

溫馨提示

  • 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ù)僅提供信息存儲(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)論