第14章多態(tài)性.ppt_第1頁
第14章多態(tài)性.ppt_第2頁
第14章多態(tài)性.ppt_第3頁
第14章多態(tài)性.ppt_第4頁
第14章多態(tài)性.ppt_第5頁
已閱讀5頁,還剩42頁未讀, 繼續(xù)免費閱讀

下載本文檔

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

文檔簡介

1、第14章 多態(tài)性,南開大學非計算機專業(yè)理工科 面向?qū)ο蟪绦蛟O(shè)計課程,2,課程內(nèi)容安排(2課時),多態(tài)性的概念 虛函數(shù) 抽象類 應(yīng)用實例 小結(jié) 學習指導,3,14.1 多態(tài)性的概念,在介紹如何在程序中實現(xiàn)多態(tài)之前,我們先給出一個實例來理解什么是多態(tài)。 【例14-1】未實現(xiàn)多態(tài)性的程序。 #include using namespace std; class Shape public: void Draw() coutDraw shapeendl; ; class Circle : public Shape public: void Draw() coutDraw circleendl; ;,4,

2、14.1 多態(tài)性的概念,class Triangle : public Shape public: void Draw() coutDraw triangleendl; ; class Rectangle : public Shape public: void Draw() coutDraw rectangleendl; ; void func(Shape ,5,14.1 多態(tài)性的概念,int main() Circle c; Triangle t; Rectangle r; func(c); func(t); func(r); return 0; 運行結(jié)果為: Draw shape Draw

3、 shape Draw shape,6,14.1 多態(tài)性的概念,顯然,我們并不希望看到這樣的結(jié)果。我們希望當執(zhí)行func(c)時調(diào)用Circle類的Draw()函數(shù),當執(zhí)行func(t)時調(diào)用Triangle類的Draw()函數(shù),當執(zhí)行func(r)時調(diào)用Rectangle類的Draw()函數(shù)。這種能夠根據(jù)對象的實際類型來調(diào)用該對象所屬類的函數(shù)、而不是每次都調(diào)用基類中函數(shù)的特性,就是本章所要介紹的多態(tài)性。,7,14.2 虛函數(shù),14.2.1 先期綁定和動態(tài)綁定 傳統(tǒng)的面向結(jié)構(gòu)的編譯器所產(chǎn)生的函數(shù)調(diào)用,采用“先期綁定”的方式。所謂“綁定”就是建立函數(shù)調(diào)用和函數(shù)本體的關(guān)聯(lián)。如果綁定發(fā)生于程序運行之

4、前(由編譯器和連接器完成),則稱為“先期綁定”。 面向?qū)ο蟪绦蛟O(shè)計語言采用“后期綁定”,也可叫“動態(tài)綁定”技術(shù),即當調(diào)用某個對象的函數(shù)時,應(yīng)該被執(zhí)行的程序代碼會根據(jù)對象的具體類型在執(zhí)行期被確定下來。這是實現(xiàn)多態(tài)性的技術(shù)保證。 想要實現(xiàn)多態(tài),就要進行“后期綁定”,在C+中,實現(xiàn)“后期綁定”的機制是虛函數(shù)。,8,14.2 虛函數(shù),14.2.2 虛函數(shù)的工作方式,9,14.2 虛函數(shù),在圖14-1所示形狀的類層次中, Circle類、Triangle類、Rectangle類都是從基類Shape派生出來的。每個類都有它自己的Draw()函數(shù),并且繪制每種形狀的Draw()函數(shù)都大不相同。當需要繪制形狀

5、時,把各派生類對象統(tǒng)一作為基類Shape的對象處理是一種很好的方法。只需要簡單地調(diào)用基類Shape的Draw()函數(shù),而不必關(guān)心對象的實際類型,程序會動態(tài)地確定(即在執(zhí)行時確定)調(diào)用哪個派生類的Draw()函數(shù)。為了使這種行為可行,把基類中的函數(shù)Draw()聲明為虛函數(shù),然后在每個派生類中重新定義Draw()使之能夠繪制合適的形狀。 虛函數(shù)的聲明方法是在基類的函數(shù)原型前加上關(guān)鍵字virtual。,10,14.2 虛函數(shù),【例14-2】將例14-1進行簡單的修改,在基類的Draw()函數(shù)前加上關(guān)鍵字virtual #include using namespace std; class Shape

6、 public: virtual void Draw() /將Draw()函數(shù)聲明為虛函數(shù) coutDraw shapeendl; ;,11,14.2 虛函數(shù),class Circle : public Shape public: virtual void Draw() /此處的virtual可以沒有,不影響Draw()的虛函數(shù)性質(zhì) coutDraw circleendl; ; class Triangle : public Shape public: void Draw() coutDraw triangleendl; ;,12,14.2 虛函數(shù),class Rectangle : publ

7、ic Shape public: void Draw() coutDraw rectangleendl; ; void drawShape(Shape ,運行結(jié)果為: Draw circle Draw triangle Draw rectangle,13,14.2 虛函數(shù),提示: Draw()函數(shù)在基類Shape中聲明為virtual,該虛函數(shù)的性質(zhì)自動地向下帶給其派生類,所以派生類中可以省略virtual關(guān)鍵字。雖然如此,但有時為了提高程序的可讀性可以在每一層中顯式地聲明這些虛函數(shù)。 只有將派生類對象賦給基類對象引用或?qū)⑴缮悓ο蟮刂焚x給基類對象指針時,才能夠表現(xiàn)出多態(tài)性。如果將派生類對象賦

8、給基類對象,那么通過基類對象必然是調(diào)用基類中的函數(shù)。,14,14.2 虛函數(shù),有了虛函數(shù),以后如果要求drawShape()增加一個繪制新形狀的功能,只要簡單的從Shape類派生新類就可以了,而drawShape()函數(shù)不需要做任何改動。例如,增加一個五邊形類: class Patagon : public Shape public: void Draw() coutDraw patagonendl; ; 只要新增加的形狀是由Shape類派生出來的,則drawShape()函數(shù)不需要做任何修改,在主函數(shù)中如果出現(xiàn)如下語句,就會調(diào)用Patagon類對象的Draw()函數(shù)。 int main()

9、Patagon p; drawShape(p); return 0; 可見,多態(tài)性使應(yīng)用程序代碼大大簡化,使程序擴充變得更加簡單。,15,14.2 虛函數(shù),14.2.3 虛析構(gòu)函數(shù) 通過上面的介紹,我們已經(jīng)了解了多態(tài)性和虛函數(shù)的工作方式。我們再來看一個例子: 【例14-3】Employee類及其派生類Manager。 #include using namespace std; class Employee public: Employee(char *pName,long no) m_name=new charstrlen(pName)+1; strcpy(m_name,pName); m_n

10、umber=no; ,16,14.2 虛函數(shù),Employee() coutEmployee destructed!endl; delete m_name; virtual void DispInfo() coutName is m_name; cout, No. is m_numberendl; protected: char *m_name; long m_number; ;,17,14.2 虛函數(shù),class Manager:public Employee public: Manager(char *pName,long no, char *dept):Employee(pName,no

11、) m_department=new charstrlen(dept)+1; strcpy(m_department,dept); Manager() coutManager destructed!endl; delete m_department; ,18,14.2 虛函數(shù),virtual void DispInfo() coutDispInfo();/ 通過基類指針,調(diào)用派生類相應(yīng)虛函數(shù) delete pe;/ 釋放指針pe所指向的地址 return 0; ,19,14.2 虛函數(shù),在Employee類和Manager類中,都包含有指針類型的成員,應(yīng)該利用析構(gòu)函數(shù)顯示地釋放這些指針所指向的

12、內(nèi)存空間。主函數(shù)中創(chuàng)建的是派生類Manager的對象,所以應(yīng)該調(diào)用Manager類的析構(gòu)函數(shù)釋放空間。但是,通過觀察運行結(jié)果,我們可以看到,被調(diào)用的是基類Employee的析構(gòu)函數(shù),也就是說,Manager類對象中,只有屬于基類Employee的那部分指針成員被釋放了,而Manager類中新添加的指針成員沒有被釋放,這樣就會導致內(nèi)存泄露問題。 要解決上述問題,可以將基類中的析構(gòu)函數(shù)聲明為虛析構(gòu)函數(shù)。這樣就會使所有派生類的析構(gòu)函數(shù)自動成為虛析構(gòu)函數(shù)(雖然它們與基類析構(gòu)函數(shù)名不同)。這時,如果像上面那樣使用delete運算符時,系統(tǒng)會根據(jù)對象的實際類型調(diào)用相應(yīng)類的析構(gòu)函數(shù)。,20,14.2 虛函數(shù)

13、,【例14-4】將例14-3中的析構(gòu)函數(shù)聲明為虛析構(gòu)函數(shù)。 virtual Employee()/ 將析構(gòu)函數(shù)聲明為虛析構(gòu)函數(shù) coutEmployee destructed!endl; delete m_name; 運行結(jié)果為: Name is Alice, No. is 2, Department is Market Employee destructed! Manager destructed! 提示: 在調(diào)用派生類對象的析構(gòu)函數(shù)后,會自動調(diào)用基類對象的析構(gòu)函數(shù)來釋放基類中定義的指針成員。,21,14.2 虛函數(shù),14.2.4 錯誤地使用虛函 如果在基類中的虛函數(shù)跟子類中的函數(shù)只是名字相

14、同,而參數(shù)不同,或者返回類型不同,即使寫上了virtual關(guān)鍵字,也不進行“后期綁定”。 【例14-5】下面程序中,基類Based的成員函數(shù)fn()和派生類SubClass的成員函數(shù)fn()參數(shù)類型不同。 #include using namespace std; class Base public: virtual void fn(int x) cout In Base class, int x = x endl; ;,22,14.2 虛函數(shù),class SubClass :public Base public: virtual void fn(float x) cout In SubCla

15、ss, float x = x endl; ; void test(Base ,23,14.2 虛函數(shù),int main() Base bc; SubClass sc; cout Calling test(bc)n; test(bc); cout Calling test(sc)n; test(sc); return 0; ,運行結(jié)果為: Calling test(bc) In Base class, int x =1 In Base class, int x =2 Calling test(sc) In Base class, int x =1 In Base class, int x =2

16、,24,14.2 虛函數(shù),關(guān)于虛函數(shù)的幾點說明: 只有類的成員函數(shù)才能聲明為虛函數(shù)。因為虛函數(shù)只適用于有繼承關(guān)系的類對象,所以普通函數(shù)不能聲明為虛函數(shù)。 靜態(tài)成員函數(shù)不能是虛函數(shù),因為靜態(tài)成員函數(shù)不受限于某個對象。 內(nèi)聯(lián)函數(shù)不能是虛函數(shù),因為內(nèi)聯(lián)函數(shù)不能在運行中確定其位置。即使虛函數(shù)放在類的內(nèi)部定義,編譯時,仍將其看作是非內(nèi)聯(lián)的。 構(gòu)造函數(shù)不能是虛函數(shù)。因為構(gòu)造時,對象還是一片未定型的空間。只有在構(gòu)造完成后,對象才能成為一個類的名副其實的實例。,25,14.3 抽象類,對于有些類來說,它僅僅表示一個概念,將其實例化沒有任何意義。比如,圖14-1的Shape類中有Draw方法,但不同的形狀其繪制

17、方法各不相同,只有指定具體的形狀才有可能實現(xiàn)Draw方法,因此,Shape類中的Draw方法根本無法實現(xiàn),從而創(chuàng)建Shape類對象也就沒有任何意義。Shape類的唯一用途就是定義每一個形狀所共有的那些屬性和方法,并作為其他形狀類(如Circle類、Rectangle類、Triangle類)的基類。 通常將這種不需要實例化的類定義成“抽象類”。抽象類不能實例化為對象,它的唯一用途是為其他類提供合適的基類,其他類可從它這里繼承和(或)實現(xiàn)接口。與“抽象類”相對應(yīng),前面所定義的類都是“具體類”,具體類可以實例化為對象。例如,可以建立抽象基類Shape,然后從它派生出具體類Circle、Rectang

18、le和Triangle等。 一個類如果是抽象類,則該類中至少有一個成員函數(shù)是純虛函數(shù),純虛函數(shù)就是在聲明時初始化為0的虛函數(shù)。,26,14.3 抽象類,【例14-6】將例14-2進行簡單的修改,將基類Shape的Draw()函數(shù)定義為純虛函數(shù),從而使Shape類是一個抽象類。 #include using namespace std; class Shape public: virtual void Draw()=0;/將Draw()函數(shù)聲明為純虛函數(shù) ; class Circle : public Shape public: virtual void Draw() coutDraw circ

19、leendl; ;,27,14.3 抽象類,class Triangle : public Shape public: void Draw() coutDraw triangleendl; ; class Rectangle : public Shape public: void Draw() coutDraw rectangleendl; ; void drawShape(Shape ,28,14.3 抽象類,int main() /Shape s;/ 錯誤。抽象類不能實例化為對象 Circle c; Triangle t; Rectangle r; drawShape (c); drawS

20、hape (t); drawShape (r); return 0; ,29,14.3 抽象類,上面代碼中,在Shape類內(nèi)定義了一個純虛函數(shù)Draw(),則Shape類成為抽象類。此時,如果試圖通過“Shape s;”實例化Shape類對象,在編譯時系統(tǒng)就會給出錯誤信息。 如果某個類是從一個帶有純虛函數(shù)的類派生出來的,并且沒有在該派生類中提供該純虛函數(shù)的定義,則該虛函數(shù)在派生類中仍然是純虛函數(shù),因而該派生類也是一個抽象類。比如,定義Shape類的派生類TwoDimensionShape類: class TwoDimensionShape : public Shape ; TwoDimensi

21、onShape類并沒有對Shape類中的純虛函數(shù)Draw()進行定義,因此,TwoDimensionShape類也是一個抽象類。 一個類層次結(jié)構(gòu)中可以不包含任何抽象類,但是很多良好的面向?qū)ο蟮南到y(tǒng),其類層次結(jié)構(gòu)的頂部是一個抽象基類。在有些情況中,類層次結(jié)構(gòu)頂部有好幾層都是抽象類。,30,14.4 應(yīng)用實例,【例14-7】下面的范例要顯示Shape、Point、Circle、Cylinder類的層次結(jié)構(gòu)。 類層次分析: 這里類的層次結(jié)構(gòu)的頂層是抽象基類Shape。Shape類中有一個純虛函數(shù)PrintShapeName()和Print(),所以它是一個抽象基類。類Shape中還包含其他兩個虛函數(shù)

22、Area()和Volume(),它們都有默認的實現(xiàn)(返回0值)。類Point從Shape類中繼承了這兩個函數(shù)的實現(xiàn),由于點的面積和體積是0,所以這種繼承是合理的。Circle類從Point類中繼承了函數(shù)Volume(),但Circle本身提供了函數(shù)Area()的實現(xiàn)。Cylinder對函數(shù)Area()和Volume()提供了自己的實現(xiàn)。 注意: 盡管Shape是一個抽象基類,但是仍然可以包含某些成員函數(shù)的實現(xiàn),并且這些實現(xiàn)是可繼承的。Shape類以四個虛函數(shù)的形式提供了一個可繼承的接口)類層次結(jié)構(gòu)中的所有類都將包含這些虛函數(shù))。,31,14.4 應(yīng)用實例,/Shape.h #ifndef SH

23、APE_H #define SHAPE_H class Shape public: virtual double Area() return 0.0; virtual double Volume() return 0.0; / 純虛函數(shù) virtual void PrintShapeName() = 0; virtual void Print() = 0; ; #endif,32,14.4 應(yīng)用實例,/Point.h #ifndef POINT_H #define POINT_H #include using namespace std; #include shape.h class Poin

24、t : public Shape public: Point( int = 0, int = 0 ); / 缺省構(gòu)造函數(shù) void SetPoint( int, int ); int GetX() const return m_x; int GetY() const return m_y; virtual void PrintShapeName() cout Point: ; virtual void Print(); private: int m_x, m_y; / 點的x,y坐標 ; #endif,33,14.4 應(yīng)用實例,/point.cpp #include point.h #incl

25、ude using namespace std; Point:Point( int x, int y ) SetPoint( x, y ); void Point:SetPoint( int x, int y ) m_x = x; m_y = y; void Point:Print() cout ( m_x , m_y ); ,34,14.4 應(yīng)用實例,/Circle.h #ifndef CIRCLE_H #define CIRCLE_H #include point.h class Circle : public Point public: Circle( double r = 0.0, i

26、nt x = 0, int y = 0 ); void SetRadius( double ); double GetRadius(); virtual double Area(); virtual void PrintShapeName() cout Circle: ; virtual void Print(); private: double m_radius; / 圓的半徑 ; #endif,35,14.4 應(yīng)用實例,/Circle.cpp #include using namespace std; #include circle.h Circle:Circle( double r, i

27、nt a, int b ) : Point( a, b ) / 調(diào)用基類構(gòu)造函數(shù) SetRadius( r ); void Circle:SetRadius( double r ) m_radius = r 0 ? r : 0; ,36,14.4 應(yīng)用實例,double Circle:GetRadius() return m_radius; double Circle:Area() return 3.14159 * m_radius * m_radius; void Circle:Print() Point:Print(); cout ; Radius = m_radius; ,37,14.4

28、 應(yīng)用實例,/Cylinder.h #ifndef CYLINDER_H #define CYLINDER_H #include circle.h class Cylinder : public Circle public: Cylinder( double h = 0.0, double r = 0.0, int x = 0, int y = 0 ); void SetHeight( double ); double GetHeight(); virtual double Area(); virtual double Volume(); virtual void PrintShapeName

29、()cout Cylinder: ; virtual void Print(); private: double m_height; / 圓柱體的高 ; #endif,38,14.4 應(yīng)用實例,/Cylinder.cpp #include using namespace std; #include cylinder.h Cylinder:Cylinder( double h, double r, int x, int y ): Circle( r, x, y ) / 調(diào)用基類構(gòu)造函數(shù) SetHeight( h ); void Cylinder:SetHeight( double h ) m_h

30、eight = h 0 ? h : 0; double Cylinder:GetHeight() return m_height; ,39,14.4 應(yīng)用實例,double Cylinder:Area() / 圓柱體表面積 return 2 * Circle:Area() + 2 * 3.14159 *GetRadius() * m_height; double Cylinder:Volume() return Circle:Area() * m_height; void Cylinder:Print() Circle:Print(); cout ; Height = m_height; ,4

31、0,14.4 應(yīng)用實例,/ch14_7.cpp #include #include #include shape.h #include point.h #include circle.h #include cylinder.h void virtualViaPointer( Shape * ); void virtualViaReference( Shape ,41,14.4 應(yīng)用實例,circle.PrintShapeName(); / 前期綁定 circle.Print(); / 前期綁定 cout n; cylinder.PrintShapeName();/ 前期綁定 cylinder.

32、Print(); / 前期綁定 cout nn; Shape *arrayOfShapes3; / 基類的數(shù)組 / arrayOfShapes0指向Point對象 arrayOfShapes0 = ,42,14.4 應(yīng)用實例,/ arrayOfShapes2指向Cylinder對象 arrayOfShapes2 = ,43,14.4 應(yīng)用實例,void virtualViaPointer( Shape *baseClassPtr ) baseClassPtr-PrintShapeName(); baseClassPtr-Print(); cout Area() Volume() nn; void

溫馨提示

  • 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)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
  • 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論