virtual函數(shù)與多態(tài)_第1頁
virtual函數(shù)與多態(tài)_第2頁
virtual函數(shù)與多態(tài)_第3頁
virtual函數(shù)與多態(tài)_第4頁
virtual函數(shù)與多態(tài)_第5頁
已閱讀5頁,還剩29頁未讀 繼續(xù)免費閱讀

下載本文檔

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

文檔簡介

1、C+程序設計實例教程程序設計實例教程第第8章章 virtual函數(shù)與多態(tài)函數(shù)與多態(tài) 多態(tài)性是面向?qū)ο缶幊倘筇匦灾唬鄳B(tài)性是指對不同類的對象的相似問題采用“統(tǒng)一接口,不同做法”的處理方式。 本章介紹繼承結(jié)構(gòu)中基于virtaul函數(shù)的動態(tài)多態(tài)。其中包括virtaul函數(shù)、virtaul函數(shù)覆蓋、純virtaul函數(shù)以及virtaul析構(gòu)函數(shù)的使用方法。 本章的最后對于多繼承結(jié)構(gòu)中的virtual繼承做了簡單的介紹。C+程序設計實例教程程序設計實例教程知識體系知識體系 本章要點:本章要點:8.1多態(tài)概述多態(tài)概述 8.2virtual函數(shù)與函數(shù)與virtual函數(shù)表函數(shù)表 8.3virtual函數(shù)

2、的覆蓋與多態(tài)函數(shù)的覆蓋與多態(tài) 8.4區(qū)分隱藏與區(qū)分隱藏與virtual函數(shù)覆蓋函數(shù)覆蓋 8.5不能動態(tài)綁定函數(shù)參數(shù)的默認值不能動態(tài)綁定函數(shù)參數(shù)的默認值 8.6程序?qū)嵗绦驅(qū)嵗蛦T管理雇員管理 8.7純純virtual函數(shù)與抽象類函數(shù)與抽象類 8.8virtual析構(gòu)函數(shù)析構(gòu)函數(shù) C+程序設計實例教程程序設計實例教程知識體系知識體系:8.9程序?qū)嵗绦驅(qū)嵗媹D程序畫圖程序 8.10RTTI 8.11多繼承結(jié)構(gòu)中使用多繼承結(jié)構(gòu)中使用virtual析構(gòu)函數(shù)析構(gòu)函數(shù) 8.12virtual繼承和繼承和virtual基類基類 C+程序設計實例教程程序設計實例教程8.1 多態(tài)概述多態(tài)概述 多態(tài)一詞是指不同

3、的對象收到相同的消息,產(chǎn)生不同的動作。 把函數(shù)調(diào)用和具體的函數(shù)執(zhí)行代碼連接在一起的過程稱為聯(lián)編。多態(tài)從聯(lián)編的實現(xiàn)角度分為:靜態(tài)多態(tài)和動態(tài)多態(tài)。本章介紹的動態(tài)多態(tài)就是使用指向派生類對象的基類指針(或基類引用),在調(diào)用基類和派生類同時具有的同名函數(shù)時,能夠調(diào)用派生類版本的函數(shù)。C+語言為實現(xiàn)這種動態(tài)多態(tài),為用戶提供virtual函數(shù)機制,編譯器的內(nèi)部采用動態(tài)聯(lián)編來完成運行時的函數(shù)調(diào)用與函數(shù)體的綁定。 C+程序設計實例教程程序設計實例教程8.2 virtual函數(shù)與函數(shù)與virtual函數(shù)表函數(shù)表 當自定義類型中存在virtual函數(shù)或其基類中存在virtaul函數(shù)時,系統(tǒng)將為該類維護一張virtu

4、al函數(shù)表,這里可以使用的virtaul函數(shù)包括該類中聲明的virtaul函數(shù),也包括該類的基類中聲明的非private屬性的virtual函數(shù)。例如: class A public: virtual void f1(); virtual void f2(int);class B:public A;class C:public Bpublic: virtual int f3();C+程序設計實例教程程序設計實例教程 上述自定義了類型A、B和C都有各自virtual函數(shù)表,見表8.18.3。當存在virtual函數(shù)表時,該類型的對象需要多占用4個字節(jié)的空間 以便為未來動態(tài)聯(lián)編做準備。程序8.1驗

5、證了這一點。C+程序設計實例教程程序設計實例教程1 /8.1 指向指向virtual函數(shù)表的指針函數(shù)表的指針5 class A 6 public: 8 int x; 9 virtual void f1()10 11 virtual void f2(int)12 13 ;14 class B:public A15 int y;17 ;18 class C:public B19 int z;21 public:22 virtual int f3()23 return 0;25 26 ;28 int main()29 30 A a;C+程序設計實例教程程序設計實例教程31 B b; C c;33 c

6、outsizeof(a)=sizeof(a)endl;34 coutsizeof(b)=sizeof(b)endl;35 coutsizeof(c)=sizeof(c)endl;36 cout-endl;38 cout&a=&aendl;39 cout&a.x=&a.xendl;40 cout&b=&bendl;41 cout&b.x=&b.xendl;42 cout&c=&cendl;43 cout&c.x=&c.xendl;44 cout-endl;46 A a1; B b1; C c1;49 coutA類型類型virtual函數(shù)表的地址:函數(shù)表的地址:*(void *)&a)endl;5

7、0 coutA類型類型virtual函數(shù)表的地址:函數(shù)表的地址:*(void *)&a1)endl;51 coutB類型類型virtual函數(shù)表的地址:函數(shù)表的地址:*(void *)&b)endl;52 coutB類型類型virtual函數(shù)表的地址:函數(shù)表的地址:*(void *)&b1)endl;53 coutC類型類型virtual函數(shù)表的地址:函數(shù)表的地址:*(void *)&c)endl;54 coutC類型類型virtual函數(shù)表的地址:函數(shù)表的地址:*(void *)&c1)endl;55 cout-endl;57 return 0;58 顯示結(jié)果:顯示結(jié)果:sizeof(a)=

8、8sizeof(b)=12sizeof(c)=16-&a=0012FF78&a.x=0012FF7C&b=0012FF6C&b.x=0012FF70&c=0012FF5C&c.x=0012FF60-A類型類型virtual函數(shù)表的地址:函數(shù)表的地址:0046C0D8A類型類型virtual函數(shù)表的地址:函數(shù)表的地址:0046C0D8B類型類型virtual函數(shù)表的地址:函數(shù)表的地址:0046C0E4B類型類型virtual函數(shù)表的地址:函數(shù)表的地址:0046C0E4C類型類型virtual函數(shù)表的地址:函數(shù)表的地址:0046C0F0C類型類型virtual函數(shù)表的地址:函數(shù)表的地址:0046C

9、0F0-C+程序設計實例教程程序設計實例教程8.3 virtual函數(shù)的覆蓋與多態(tài)函數(shù)的覆蓋與多態(tài) 要想在繼承結(jié)構(gòu)中做到指向派生類的基類指針或引用能夠調(diào)用派生類函數(shù),僅僅在基類中將成員函數(shù)聲明為virtual函數(shù)是不夠的,還需要在派生類中“覆蓋”基類的virtual函數(shù),這里的“覆蓋”是指virtual函數(shù)表的覆蓋。當派生類的virtual函數(shù)的函數(shù)原型與其基類virtual函數(shù)的函數(shù)原型完全相同時,在派生類的virtual函數(shù)表中系統(tǒng)將使用派生類virtual函數(shù)覆蓋其基類virtual函數(shù)。 例如: C+程序設計實例教程程序設計實例教程class Apublic: virtual void

10、 f1(); virtual void f2(int);class B:public Apublic: virtual void f1(); void f2(int);class C:public Bpublic: virtual void f1(); virtual int f3(); ;C+程序設計實例教程程序設計實例教程 上例中類型A、B和C的virtual函數(shù)表參見表8.48.6。 程序8.2演示了virtual函數(shù)的覆蓋和動態(tài)多態(tài)的使用。C+程序設計實例教程程序設計實例教程1 /8.2 virtual函數(shù)的覆蓋與多態(tài)函數(shù)的覆蓋與多態(tài) 2 #include 3 using std:co

11、ut; 4 using std:endl; 5 class A 6 7 public: 8 virtual void f1() 9 10 coutvoid A:f1()被執(zhí)行被執(zhí)行endl;11 12 virtual void f2(int)13 14 coutvoid A:f2(int)被執(zhí)行被執(zhí)行endl;15 16 ;17 class B:public A18 19 public:20 virtual void f1()21 22 coutvoid B:f1()被執(zhí)行被執(zhí)行endl;23 C+程序設計實例教程程序設計實例教程24 void f2(int)25 26 coutvoid B:

12、f2(int)被執(zhí)行被執(zhí)行endl;27 28 ;29 class C:public B30 31 public:32 virtual void f1()33 34 coutvoid C:f1()被執(zhí)行被執(zhí)行endl;35 36 virtual int f3()37 38 coutint C:f3()被執(zhí)行被執(zhí)行endl;39 return 0;40 41 ;42 43 int main()44 45 A* p3;46 p0=new A;C+程序設計實例教程程序設計實例教程47 p1=new B;48 p2=new C;49 50 for(int i=0;if1();53 pi-f2(0);5

13、4 55 cout-f3();編譯錯誤編譯錯誤58 (C*)p2)-f3();59 cout-A:f1();62 p2-A:f1();63 /p2-B:f1();編譯錯誤編譯錯誤64 cout-endl;65 66 for( i=0;i3;+i)67 delete pi;68 return 0;69 顯示結(jié)果:顯示結(jié)果:void A:f1()被執(zhí)行被執(zhí)行void A:f2(int)被執(zhí)行被執(zhí)行void B:f1()被執(zhí)行被執(zhí)行void B:f2(int)被執(zhí)行被執(zhí)行void C:f1()被執(zhí)行被執(zhí)行void B:f2(int)被執(zhí)行被執(zhí)行-int C:f3()被執(zhí)行被執(zhí)行-void A:f1(

14、)被執(zhí)行被執(zhí)行void A:f1()被執(zhí)行被執(zhí)行-C+程序設計實例教程程序設計實例教程8.4 區(qū)分隱藏與區(qū)分隱藏與virtual函數(shù)覆蓋函數(shù)覆蓋 當成員函數(shù)為非virtual函數(shù)時,派生類和基類的同名成員函數(shù)一定是“隱藏”關(guān)系;當成員函數(shù)為virtual函數(shù)時,派生類和基類的同名成員函數(shù)可能是“隱藏”關(guān)系也可能是“覆蓋”關(guān)系。當派生類和其基類中virtual函數(shù)的原型的返回值類型不同時,屬于編譯錯誤;參數(shù)列表不同,或是一個為const成員函數(shù),另一個為非const成員函數(shù)時,屬于“隱藏”關(guān)系。 程序8.3驗證這一點。 C+程序設計實例教程程序設計實例教程5 class A 6 public:

15、8 virtual void f1() 9 coutvoid A:f1()被執(zhí)行被執(zhí)行endl;11 12 virtual void f2()13 coutvoid A:f2()被執(zhí)行被執(zhí)行endl;15 16 void f3()17 coutvoid A:f3()被執(zhí)行被執(zhí)行endl;19 20 virtual void f4()const21 coutvoid A:f4()被執(zhí)行被執(zhí)行endl;23 24 virtual void f5()25 coutvoid A:f5()被執(zhí)行被執(zhí)行endl;27 28 virtual void f6(int)29 coutvoid A:f6(int)

16、被執(zhí)行被執(zhí)行endl;31 32 virtual void f7(int=0)C+程序設計實例教程程序設計實例教程33 coutvoid A:f7(int)被執(zhí)行被執(zhí)行endl;35 36 virtual void f8()37 coutvoid A:f8()被執(zhí)行被執(zhí)行endl;39 ;41 class B:public A42 public:44 virtual void f1()45 coutvoid B:f1()被執(zhí)行被執(zhí)行endl;47 48 void f2()49 coutvoid B:f2()被執(zhí)行被執(zhí)行endl;51 52 virtual void f3()53 coutvoi

17、d B:f3()被執(zhí)行被執(zhí)行endl;55 56 virtual void f4()57 coutvoid B:f4()被執(zhí)行被執(zhí)行endl;59 60 virtual void f5()const61 coutvoid B:f5()被執(zhí)行被執(zhí)行endl; 顯示結(jié)果:顯示結(jié)果:類類A的構(gòu)造函數(shù)被調(diào)用的構(gòu)造函數(shù)被調(diào)用類類B的的1個參數(shù)的構(gòu)造函數(shù)被調(diào)用個參數(shù)的構(gòu)造函數(shù)被調(diào)用b1.x=0,b1.y=1類類B的析構(gòu)函數(shù)被調(diào)用的析構(gòu)函數(shù)被調(diào)用類類A的析構(gòu)函數(shù)被調(diào)用的析構(gòu)函數(shù)被調(diào)用-類類A的構(gòu)造函數(shù)被調(diào)用的構(gòu)造函數(shù)被調(diào)用類類B的的2個參數(shù)的構(gòu)造函數(shù)被調(diào)用個參數(shù)的構(gòu)造函數(shù)被調(diào)用b2.x=10,b2.y=20

18、類類B的析構(gòu)函數(shù)被調(diào)用的析構(gòu)函數(shù)被調(diào)用類類A的析構(gòu)函數(shù)被調(diào)用的析構(gòu)函數(shù)被調(diào)用-C+程序設計實例教程程序設計實例教程64 virtual void f6(char)65 coutvoid B:f6(char)被執(zhí)行被執(zhí)行endl;67 68 virtual void f7()69 coutvoid B:f7()被執(zhí)行被執(zhí)行endl;71 72 /*編譯錯誤編譯錯誤73 virtual int f8()74 coutvoid B:f8()被執(zhí)行被執(zhí)行f1();85 p-f2();86 p-f3();87 p-f4();88 p-f5();89 p-f6(A);90 p-f7();91 p-f8()

19、;92 delete p;93 return 0;94 顯示結(jié)果:顯示結(jié)果:void B:f1()被執(zhí)行被執(zhí)行void B:f2()被執(zhí)行被執(zhí)行void A:f3()被執(zhí)行被執(zhí)行void A:f4()被執(zhí)行被執(zhí)行void A:f5()被執(zhí)行被執(zhí)行void A:f6(int)被執(zhí)行被執(zhí)行void A:f7(int)被執(zhí)行被執(zhí)行void A:f8()被執(zhí)行被執(zhí)行 C+程序設計實例教程程序設計實例教程8.5 不能動態(tài)綁定函數(shù)參數(shù)的默認值不能動態(tài)綁定函數(shù)參數(shù)的默認值 當派生類的virtual函數(shù)覆蓋其基類的virtual函數(shù),并且這兩個函數(shù)的參數(shù)均有默認值時,雖然系統(tǒng)將動態(tài)綁定函數(shù)體,但是參數(shù)的默認值

20、仍然靜態(tài)綁定。也就是說,當“覆蓋”發(fā)生時,指向派生類的基類指針或引用調(diào)用派生類版本的函數(shù),但是如果函數(shù)參數(shù)取默認值調(diào)用時,參數(shù)值取基類版本中的默認值。 程序8.4驗證這一點。C+程序設計實例教程程序設計實例教程5 class A 6 public: 8 virtual void f(int n=0) const 9 coutn=nendl;11 coutvoid A:f(int n=0)被執(zhí)行被執(zhí)行endl;12 14 ;15 class B:public A16 17 public:18 virtual void f(int n=10) const19 coutn=nendl;21 cout

21、void B:f(int n=10)被執(zhí)行被執(zhí)行f();29 delete p;30 return 0;31 顯示結(jié)果:顯示結(jié)果:n=0void B:f(int n=10)被執(zhí)行被執(zhí)行C+程序設計實例教程程序設計實例教程8.7 純純virtual函數(shù)與抽象類函數(shù)與抽象類 為了在繼承結(jié)構(gòu)中使用動態(tài)多態(tài),我們需要在基類中聲明virtual函數(shù),并在派生類中覆蓋它們。有時,基類的virtual函數(shù)存在的目的就是為了被派生類的同名virtual函數(shù)覆蓋,而自己本身沒有特別要實現(xiàn)的功能,同時,這種基類存在的目的僅僅為了派生其它類型,而本身不需要實例化。 程序8.6演示純virtual函數(shù)的聲明方法,并驗

22、證抽象類不能實例化。C+程序設計實例教程程序設計實例教程1 /8.6 純純virtual函數(shù)與抽象類函數(shù)與抽象類 2 #include 3 using std:cout; 4 using std:endl; 5 6 class A 7 8 public: 9 virtual void f()const =0;/純純virtual函數(shù)函數(shù)10 ;11 12 class B1:public A13 14 virtual void f()15 16 coutB1:f()被執(zhí)行被執(zhí)行endl;17 18 ;19 20 class B2:public AC+程序設計實例教程程序設計實例教程21 22 p

23、ublic:23 virtual void f()const24 25 coutB2:f()被執(zhí)行被執(zhí)行f();38 delete p;39 return 0;40 顯示結(jié)果:顯示結(jié)果:B2:f()被執(zhí)行被執(zhí)行C+程序設計實例教程程序設計實例教程8.8 virtual析構(gòu)函數(shù) 在派生類存在析構(gòu)函數(shù),并且在派生類對象釋放時必須調(diào)用的情況下,我們需要將基類的析構(gòu)函數(shù)設為virtual析構(gòu)函數(shù)。 virtual析構(gòu)函數(shù)的設置方法很簡單,只需要在析構(gòu)函數(shù)的聲明前加入關(guān)鍵字virtual即可,基類的析構(gòu)函數(shù)為virtual函數(shù)后,派生類的析構(gòu)函數(shù)自動成為virtual析構(gòu)函數(shù),并且覆蓋基類的析構(gòu)函數(shù)。

24、程序8.7演示了virtual析構(gòu)函數(shù)的使用方法。C+程序設計實例教程程序設計實例教程1 /8.7 virtual析構(gòu)函數(shù)析構(gòu)函數(shù)6 class A1 7 8 public: 9 A1()10 11 coutA1()被執(zhí)行被執(zhí)行endl;12 13 ;15 class B1:public A116 17 B1()18 19 coutB1()被執(zhí)行被執(zhí)行endl;20 21 ;23 class A224 25 public:C+程序設計實例教程程序設計實例教程26 virtual A2()27 coutA2()被執(zhí)行被執(zhí)行endl;29 30 ;32 class B2:public A233 3

25、4 B2()35 coutB2()被執(zhí)行被執(zhí)行endl;37 38 ;40 int main()41 A1 *p1=new B1;43 delete p1;44 cout-endl;46 A2 *p2=new B2;47 delete p2;48 cout-endl;49 return 0;50 顯示結(jié)果:顯示結(jié)果:A1()被執(zhí)行被執(zhí)行-B2()被執(zhí)行被執(zhí)行A2()被執(zhí)行被執(zhí)行-C+程序設計實例教程程序設計實例教程8.9 程序?qū)嵗绦驅(qū)嵗媹D程序畫圖程序 畫圖程序是講解動態(tài)多態(tài)的經(jīng)典程序,本節(jié)的程序8.8中給出簡單的畫圖程序,該程序中將實現(xiàn)“圖形”基類Shape(抽象類),并且派生了“點”類P

26、oint、“直線”類Line和“圓”類Circle等3個具體類(與抽象類相反,能夠?qū)嵗念悾?本例中使用動態(tài)多態(tài)的方法處理各種“圖形”的“畫”方法,并結(jié)合純virtual函數(shù)和virtual析構(gòu)函數(shù)的知識。同時,由于本例的重點是繼承結(jié)構(gòu)中動態(tài)多態(tài)的實現(xiàn)方法,而不是真的要在輸出設備上畫圖,因此,本例中的“畫”方法僅僅是使用文字進行輸出,而沒有調(diào)用C+中關(guān)于圖形顯示的函數(shù)。C+程序設計實例教程程序設計實例教程8.10 RTTI 在繼承結(jié)構(gòu)中使用動態(tài)多態(tài),我們總是使用基類指針指向派生類對象,這樣可以以相同的方式處理不同派生類對象的類似問題。 但是,畢竟使用動態(tài)多態(tài)的繼承結(jié)構(gòu)中,基類指針指向的是不同類型的派生類對象,很有可能某個類的派生類對象存在特殊的問題需要處理 。 解決上述問題需要使用C+語言提供的RTTI機制,RTTI是指運行時的類型信息,即在運行時動態(tài)判斷基類指針(或引用)到底指向的是哪一種派生類對象,以便做出正確的處理方法。 C+程序設計實例教程程序設計實例教程8.11 多繼承結(jié)構(gòu)中使用virtual析構(gòu)函數(shù) 一個派生類同時擁有多個直接基類的情況稱為多繼承。在多繼承的結(jié)構(gòu)中,只要派生類諸多基類中的某一個存在必須被調(diào)用的析構(gòu)函數(shù),這些基類的析構(gòu)函數(shù)都應該設為virtual析構(gòu)函數(shù)。因為,在多繼承的結(jié)構(gòu)中,我們可能使用任意的基類指針指向派生類對象,在將該指針作為dele

溫馨提示

  • 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

提交評論