第九章0522程序設(shè)計(jì)課件_第1頁(yè)
第九章0522程序設(shè)計(jì)課件_第2頁(yè)
第九章0522程序設(shè)計(jì)課件_第3頁(yè)
第九章0522程序設(shè)計(jì)課件_第4頁(yè)
第九章0522程序設(shè)計(jì)課件_第5頁(yè)
已閱讀5頁(yè),還剩64頁(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+ 6- 1 第九章第九章 多態(tài)多態(tài)C+ 6- 2 9.1 多態(tài)性的概念多態(tài)性的概念9.2 重載、覆蓋與靜態(tài)聯(lián)編重載、覆蓋與靜態(tài)聯(lián)編9.3 虛函數(shù)與運(yùn)行時(shí)多態(tài)虛函數(shù)與運(yùn)行時(shí)多態(tài)9.4 純虛函數(shù)與抽象類(lèi)純虛函數(shù)與抽象類(lèi)9.5 模板模板C+ 6- 3 9.1 多態(tài)性的概念多態(tài)性的概念多態(tài)性多態(tài)性( polymorphism ) 是面向?qū)ο蟪绦蛟O(shè)計(jì)的重要是面向?qū)ο蟪绦蛟O(shè)計(jì)的重要特征。特征。C+支持多態(tài)性,利用多態(tài)性,可以設(shè)計(jì)和擴(kuò)展一個(gè)支持多態(tài)性,利用多態(tài)性,可以設(shè)計(jì)和擴(kuò)展一個(gè)易于擴(kuò)展的系統(tǒng)。易于擴(kuò)展的系統(tǒng)。l什么叫多態(tài)?什么叫多態(tài)?u多態(tài)的意思是一種事物的多種形態(tài)。多態(tài)的意思是一種事

2、物的多種形態(tài)。u在在C+中,中,是指具有不同功能的函數(shù)可以用同一個(gè)函數(shù)名是指具有不同功能的函數(shù)可以用同一個(gè)函數(shù)名。u面向?qū)ο蠓椒ㄖ幸话闶沁@樣描述多態(tài)性的:向不同的對(duì)象面向?qū)ο蠓椒ㄖ幸话闶沁@樣描述多態(tài)性的:向不同的對(duì)象發(fā)送同一個(gè)消息,不同的對(duì)象在接收時(shí)會(huì)產(chǎn)生不同的行為發(fā)送同一個(gè)消息,不同的對(duì)象在接收時(shí)會(huì)產(chǎn)生不同的行為(即方法)。(即方法)。C+ 6- 4 9.1.1 面向?qū)ο蟪绦蛟O(shè)計(jì)中的多態(tài)面向?qū)ο蟪绦蛟O(shè)計(jì)中的多態(tài)我們其實(shí)已經(jīng)接觸過(guò)多態(tài)性的現(xiàn)象。如函數(shù)的重載,我們其實(shí)已經(jīng)接觸過(guò)多態(tài)性的現(xiàn)象。如函數(shù)的重載,運(yùn)算符的重載。運(yùn)算符的重載。l多態(tài)性分類(lèi)多態(tài)性分類(lèi):從系統(tǒng)實(shí)現(xiàn)的角度看,多態(tài)性分為以下兩類(lèi):

3、從系統(tǒng)實(shí)現(xiàn)的角度看,多態(tài)性分為以下兩類(lèi):u靜態(tài)多態(tài)性:又稱(chēng)編譯時(shí)的多態(tài)性。如函數(shù)重載和運(yùn)算符靜態(tài)多態(tài)性:又稱(chēng)編譯時(shí)的多態(tài)性。如函數(shù)重載和運(yùn)算符重載,都屬于靜態(tài)多態(tài)性。重載,都屬于靜態(tài)多態(tài)性。u動(dòng)態(tài)多態(tài)性:有稱(chēng)為運(yùn)行時(shí)的多態(tài)性。它主要表現(xiàn)為虛函動(dòng)態(tài)多態(tài)性:有稱(chēng)為運(yùn)行時(shí)的多態(tài)性。它主要表現(xiàn)為虛函數(shù)數(shù)( virtual function )。C+ 6- 5 9.1.1 面向?qū)ο蟪绦蛟O(shè)計(jì)中的多態(tài)面向?qū)ο蟪绦蛟O(shè)計(jì)中的多態(tài)l面向?qū)ο蟮亩鄳B(tài)可以分為四類(lèi):面向?qū)ο蟮亩鄳B(tài)可以分為四類(lèi): (1)重載多態(tài))重載多態(tài) (2)強(qiáng)制多態(tài))強(qiáng)制多態(tài) (3)包含多態(tài)(或運(yùn)行(時(shí))多態(tài))包含多態(tài)(或運(yùn)行(時(shí))多態(tài))(4)參數(shù)多

4、態(tài))參數(shù)多態(tài) l普通函數(shù)及類(lèi)的成員函數(shù)重載、運(yùn)算符重載屬于重載多態(tài)普通函數(shù)及類(lèi)的成員函數(shù)重載、運(yùn)算符重載屬于重載多態(tài)l類(lèi)型強(qiáng)制轉(zhuǎn)換屬于強(qiáng)制多態(tài)類(lèi)型強(qiáng)制轉(zhuǎn)換屬于強(qiáng)制多態(tài) l虛函數(shù)屬于包含多態(tài)虛函數(shù)屬于包含多態(tài) l函數(shù)模板和類(lèi)模板屬于參數(shù)多態(tài)函數(shù)模板和類(lèi)模板屬于參數(shù)多態(tài)C+ 6- 6 9.1.2 多態(tài)性的實(shí)現(xiàn)多態(tài)性的實(shí)現(xiàn)聯(lián)編聯(lián)編l主程序在調(diào)用子程序之前,一般要:保存現(xiàn)場(chǎng)拿到子程序的地址轉(zhuǎn)向子程序執(zhí)行。l在C + 中,普通的函數(shù)調(diào)用由C + 鏈接器在鏈接過(guò)程中將子程序(被調(diào)函數(shù))的內(nèi)存(邏輯)地址放到主程序(主調(diào)函數(shù))的代碼中。l如果主程序調(diào)用子程序的語(yǔ)句是多態(tài)性語(yǔ)句,那么在執(zhí)行調(diào)用之前,主程序必須

5、得確定子程序的地址(或者說(shuō),確定調(diào)用哪個(gè)子程序),這個(gè)過(guò)程稱(chēng)為聯(lián)編(Binding),也可翻譯成“綁定”。C+ 6- 7 9.1.2 多態(tài)性的實(shí)現(xiàn)多態(tài)性的實(shí)現(xiàn)聯(lián)編聯(lián)編l聯(lián)編有兩種方式:靜態(tài)聯(lián)編和動(dòng)態(tài)聯(lián)編。(1)在源程序編譯的時(shí)候就能確定具有多態(tài)性的語(yǔ)句調(diào)用哪個(gè)函數(shù),稱(chēng)為靜態(tài)聯(lián)編。函數(shù)重載和模板都是在編譯時(shí)確定被調(diào)函數(shù),所以屬于靜態(tài)聯(lián)編。(2)在程序運(yùn)行時(shí)才能夠確定具有多態(tài)性的語(yǔ)句究竟調(diào)用哪個(gè)函數(shù),稱(chēng)為動(dòng)態(tài)聯(lián)編。用動(dòng)態(tài)聯(lián)編實(shí)現(xiàn)的多態(tài),也稱(chēng)為運(yùn)行時(shí)多態(tài)(Runtime Polymorphism)。l聯(lián)編與多態(tài)之間的關(guān)系可以用圖9-1 簡(jiǎn)明地描述。C+ 6- 8 9.2 重載、覆蓋與靜態(tài)聯(lián)編重載、覆

6、蓋與靜態(tài)聯(lián)編9.2.1 重載與靜態(tài)聯(lián)編重載與靜態(tài)聯(lián)編以下是一個(gè)重載函數(shù)的例子。int add(int a)return a+10;int add(int a,int b)return a+b;int main()int x=1,y=2;add(x);add(x,y);return 0;l上述代碼中有兩個(gè)重載函數(shù)add(),在main()函數(shù)中調(diào)用了這兩個(gè)重載函數(shù)。編譯器根據(jù)函數(shù)的參數(shù)能夠確定每次調(diào)用哪個(gè)函數(shù),而每個(gè)函數(shù)都有自己的地址,編譯器只要加上轉(zhuǎn)移到相應(yīng)地址的“轉(zhuǎn)移指令”,就可以完成對(duì)不同重載函數(shù)的調(diào)用。l由此可見(jiàn),所謂的“同名”僅僅是C + 的特性,經(jīng)過(guò)編譯器處理之后,同名的函數(shù)變成了不

7、同地址的子程序,對(duì)同名函數(shù)的調(diào)用變成了對(duì)不同地址子程序的調(diào)用。這個(gè)轉(zhuǎn)變過(guò)程是由編譯器完成的,換句話說(shuō),編編譯器分辨出了同名函數(shù)的不同之處譯器分辨出了同名函數(shù)的不同之處。這屬于靜態(tài)聯(lián)編。C+ 6- 9 9.2 重載、覆蓋與靜態(tài)聯(lián)編重載、覆蓋與靜態(tài)聯(lián)編9.2.1 覆蓋與靜態(tài)聯(lián)編覆蓋與靜態(tài)聯(lián)編l覆蓋現(xiàn)象只能出現(xiàn)在繼承樹(shù)中。在派生類(lèi)中定義和基類(lèi)中同名的成員函數(shù),是對(duì)基類(lèi)進(jìn)行改造,為派生類(lèi)增加新行為的一種常用的方法。通過(guò)不同的派生類(lèi)的對(duì)象(對(duì)象指針或者對(duì)象引用),調(diào)用這些同名的成員函數(shù),實(shí)現(xiàn)不同的操作,也是多態(tài)性的一種表現(xiàn)。l在某些情況下,覆蓋會(huì)導(dǎo)致靜態(tài)聯(lián)編;而另外一些情況下,則會(huì)導(dǎo)致動(dòng)態(tài)聯(lián)編。C+ 6

8、- 10 9.2 重載、覆蓋與靜態(tài)聯(lián)編重載、覆蓋與靜態(tài)聯(lián)編9.2.1 覆蓋與靜態(tài)聯(lián)編覆蓋與靜態(tài)聯(lián)編l例9-19-1 用對(duì)象訪問(wèn)繼承樹(shù)中的同名函數(shù)。/以下代碼僅為示例,無(wú)法編譯class B public: f().; /基類(lèi)class P: public B public: f().; /派生類(lèi)1class Q: public B public: f().; /派生類(lèi)2main () P p; Q q; /創(chuàng)建派生類(lèi)對(duì)象p.f(); /調(diào)用P:f()q.f(); /調(diào)用Q:f()l基類(lèi)B 派生出類(lèi)P 和類(lèi)Q,這3 個(gè)類(lèi)中都有同名函數(shù)f()。在類(lèi)的外部,通過(guò)派生類(lèi)的對(duì)象調(diào)用同名函數(shù),那么該調(diào)用被

9、編譯器在編譯階段鏈接到該對(duì)象所屬類(lèi)的同名函數(shù)上。這種情況下,編譯器執(zhí)行了靜態(tài)聯(lián)編。l通過(guò)對(duì)象調(diào)用類(lèi)的同名函數(shù),一定是本類(lèi)通過(guò)對(duì)象調(diào)用類(lèi)的同名函數(shù),一定是本類(lèi)中的函數(shù)。中的函數(shù)。C+ 6- 11 9.2 重載、覆蓋與靜態(tài)聯(lián)編重載、覆蓋與靜態(tài)聯(lián)編9.2.1 覆蓋與靜態(tài)聯(lián)編覆蓋與靜態(tài)聯(lián)編例9-29-2 用指針訪問(wèn)繼承樹(shù)中的同名函數(shù)。/以下代碼僅為示例,無(wú)法編譯class B public: f(). ; /基類(lèi)class P: public B public: f(). ; /派生類(lèi)1class Q: public B public: f(). ; /派生類(lèi)2main () B* b_ptr; P

10、p; Q q; /定義基類(lèi)的指針和派生類(lèi)的對(duì)象b_ptr=&p; /基類(lèi)指針指向派生類(lèi)b_ptr-f(); /通過(guò)基類(lèi)指針調(diào)用B:f()b_ptr=&q; /基類(lèi)指針指向派生類(lèi)b_ptr-f(); /通過(guò)基類(lèi)指針調(diào)用B:f()l基類(lèi)B 派生出類(lèi)P 和類(lèi)Q,這3 個(gè)類(lèi)中都有同名函數(shù)f()。在類(lèi)的外部,用派生類(lèi)對(duì)象的地址為派生類(lèi)對(duì)象的地址為基類(lèi)指針賦值,隨后,基類(lèi)指針賦值,隨后,使用該基類(lèi)指針調(diào)用同使用該基類(lèi)指針調(diào)用同名函數(shù)。此時(shí)所調(diào)用的名函數(shù)。此時(shí)所調(diào)用的同名函數(shù)是基類(lèi)的同名同名函數(shù)是基類(lèi)的同名函數(shù)。函數(shù)。原因在于,用派生類(lèi)對(duì)象的地址為基類(lèi)指針賦值時(shí),實(shí)際上進(jìn)行了派生類(lèi)對(duì)象向基類(lèi)

11、對(duì)象的轉(zhuǎn)換(參見(jiàn)8.4.2 小節(jié))。上述情況下,編譯器也執(zhí)行了靜態(tài)聯(lián)編。C+ 6- 12 9.2.3 一個(gè)典型的例子一個(gè)典型的例子現(xiàn)在來(lái)看一個(gè)綜合性的示例,請(qǐng)同學(xué)們認(rèn)真讀懂這個(gè)現(xiàn)在來(lái)看一個(gè)綜合性的示例,請(qǐng)同學(xué)們認(rèn)真讀懂這個(gè)程序。程序。我們先建立一個(gè)我們先建立一個(gè)point ( 點(diǎn)點(diǎn) )類(lèi),包含數(shù)據(jù)成員類(lèi),包含數(shù)據(jù)成員x,y ( 坐坐標(biāo)點(diǎn)標(biāo)點(diǎn) )。以它為基類(lèi),派生出一個(gè)。以它為基類(lèi),派生出一個(gè)circle ( 圓圓 )類(lèi),增加數(shù)據(jù)類(lèi),增加數(shù)據(jù)成員成員 r (半徑)。再以(半徑)。再以circle 類(lèi)為直接基類(lèi),派生出一個(gè)類(lèi)為直接基類(lèi),派生出一個(gè)cylinder ( 圓柱體圓柱體 )類(lèi),增加數(shù)據(jù)成

12、員類(lèi),增加數(shù)據(jù)成員 h ( 高高 )。要求重載運(yùn)。要求重載運(yùn)算符算符“”,使之能輸出這些對(duì)象。,使之能輸出這些對(duì)象。這個(gè)題目程序較長(zhǎng),我們分為若干步驟進(jìn)行,分步調(diào)這個(gè)題目程序較長(zhǎng),我們分為若干步驟進(jìn)行,分步調(diào)試:試:先聲明基類(lèi)先聲明基類(lèi)point類(lèi),并調(diào)試之;類(lèi),并調(diào)試之;再聲明派生類(lèi)再聲明派生類(lèi)circle類(lèi),調(diào)試之;類(lèi),調(diào)試之;最后聲明最后聲明cylinder類(lèi),調(diào)試之。類(lèi),調(diào)試之。C+ 6- 13 先聲明基類(lèi)先聲明基類(lèi)point#include using namespace std;class point /聲明類(lèi)聲明類(lèi)pointpublic: point (float x=0, fl

13、oat y=0 ); /有默認(rèn)值的構(gòu)造函數(shù)有默認(rèn)值的構(gòu)造函數(shù) void setPoint (float, float); /設(shè)置點(diǎn)坐標(biāo)設(shè)置點(diǎn)坐標(biāo) float getX ( ) const return x; / 讀讀x 值值 float getY ( ) const return y; / 讀讀y 值值 friend ostream & operator ( ostream &, const point &);protected: float x,y;/ 下面定義下面定義 point類(lèi)的成員函數(shù)類(lèi)的成員函數(shù)point : point (float a, float b)

14、 / point的構(gòu)造函數(shù)的構(gòu)造函數(shù) x = a; y = b; void point : setPoint (float a, float b) x =a; y = b; ostream &operator (ostream &output, const point &p) / 重載運(yùn)算符重載運(yùn)算符 output“x=“p.x“, y=“p.y“”endl; return output; 編寫(xiě)好左邊程編寫(xiě)好左邊程序后,可調(diào)試它的序后,可調(diào)試它的正確性。我們編寫(xiě)正確性。我們編寫(xiě)一個(gè)臨時(shí)的一個(gè)臨時(shí)的main函數(shù):函數(shù):Void main( ) point p(1.1,2.

15、2); coutpendl; p.setPoint(3.3,4.4); coutpendl; 運(yùn)行通過(guò)后,運(yùn)行通過(guò)后,刪除臨時(shí)的刪除臨時(shí)的main函數(shù),進(jìn)入下一步函數(shù),進(jìn)入下一步工作。工作。C+ 6- 14 再聲明派生類(lèi)再聲明派生類(lèi)circle類(lèi)類(lèi)class circle : public point /聲明類(lèi)聲明類(lèi)citclepublic: circle (float x=0, float y=0, float r=0); /構(gòu)造函數(shù)構(gòu)造函數(shù) void setRadius ( float); /設(shè)置圓半徑設(shè)置圓半徑 float getRadius ( ) const return radiu

16、s; / 讀讀 r值值 float area ( ) const; / 計(jì)算圓面積計(jì)算圓面積 friend ostream & operator ( ostream &, const circle &);protected: float radius;/ 下面定義下面定義 circle類(lèi)的成員函數(shù)類(lèi)的成員函數(shù)circle : circle (float a, float b, float r) : point (a,b),radius(r) / point的構(gòu)造函數(shù)的構(gòu)造函數(shù)void circle : setRadius (float r) radius = r; fl

17、oat circle : area( ) const / 計(jì)算圓面積計(jì)算圓面積 return 3.14159*radius*radius; ostream &operator (ostream &output, const circle &c) / 重載運(yùn)算符重載運(yùn)算符 output“x=“c.x“, y=“c.y“, r=” c.radius“, area=“c.area( )endl; return output; 編寫(xiě)好左邊程編寫(xiě)好左邊程序后,可調(diào)試它的序后,可調(diào)試它的正確性。我們編寫(xiě)正確性。我們編寫(xiě)一個(gè)臨時(shí)的一個(gè)臨時(shí)的main函函數(shù):數(shù):Void main( )

18、 circle c(1.1,2.2,1.0); c.setRadius(2.0); c.setPoint(2.0, 2.0 ); coutcendl; 運(yùn)行通過(guò)后,運(yùn)行通過(guò)后,刪除臨時(shí)的刪除臨時(shí)的main函函數(shù),進(jìn)入下一步工數(shù),進(jìn)入下一步工作。作。C+ 6- 15 最后聲明最后聲明cylinder類(lèi)類(lèi)class cylinder : public circle /聲明類(lèi)聲明類(lèi)cylinderpublic: cylinder (float x=0, float y=0, float r=0, float h=0 ); /構(gòu)造函數(shù)構(gòu)造函數(shù) void setHeight ( float h) hei

19、ght = h; /設(shè)置圓柱體高設(shè)置圓柱體高 float getHeight ( ) const return height; / 讀讀 r值值 float area ( ) const; / 計(jì)算圓柱表面積計(jì)算圓柱表面積 float volume ( ) const; /求體積求體積 friend ostream & operator ( ostream &, const cylinder &);protected: float height;/ 下面定義下面定義 cylinder類(lèi)的成員函數(shù)類(lèi)的成員函數(shù)cylinder : cylinder (float a, fl

20、oat b, float r,float h) : circle (a,b,r), height (h) / point的構(gòu)造函數(shù)的構(gòu)造函數(shù)float cylinder : area( ) const / 計(jì)算圓柱表面積計(jì)算圓柱表面積 return 2* circle : area( )+2*3.14159*radius*height; Float cylinder : volume( ) const return circle:area( )*height; / 求圓柱體積求圓柱體積ostream &operator (ostream &output, const cylin

21、der &cy) / 重載運(yùn)算符重載運(yùn)算符 output“x=“cy.x“, y=“cy.y“, r=” cy.radius “, height=“cy.height“, area=“cy.area( ) “,volume=“cy.volume( )endl; return output;C+ 6- 16 最后寫(xiě)出主函數(shù)最后寫(xiě)出主函數(shù)void main( ) cylinder cy1(3.0, 6.0, 2.0, 10.0 ); cout“original cylinder: n“cy1; cy1.setHeight(15.0); cy1.setRadius(7.5); cy1.set

22、Point(5, 5); cout“new cylinder: n“cy1; point &pRef = cy1; cout“n pRef as a point:”pRef; circle &cRef =cy1; cout“n cRef as a circle:”cRef;C+ 6- 17 9.3 虛函數(shù)與運(yùn)行時(shí)多態(tài)虛函數(shù)與運(yùn)行時(shí)多態(tài)上節(jié)介紹的程序中,上節(jié)介紹的程序中,circle 類(lèi)和類(lèi)和 cylinder 類(lèi)都定義了類(lèi)都定義了 area 函數(shù),但功能不一樣。前者是求圓面積,后者是求圓柱函數(shù),但功能不一樣。前者是求圓面積,后者是求圓柱體表面積。由于處在不同的類(lèi)中,同名是合法的

23、。體表面積。由于處在不同的類(lèi)中,同名是合法的。如果在圓柱體對(duì)象如果在圓柱體對(duì)象 cy1中要調(diào)用求圓面積的函數(shù),則應(yīng)中要調(diào)用求圓面積的函數(shù),則應(yīng)該寫(xiě)成:該寫(xiě)成:cy1.circle:area( ),而不能寫(xiě)成,而不能寫(xiě)成cy1.area( )。用這。用這種方法來(lái)區(qū)分兩種同名函數(shù)。使用起來(lái)不方便。種方法來(lái)區(qū)分兩種同名函數(shù)。使用起來(lái)不方便。人們要問(wèn),能否用一個(gè)調(diào)用形式,既能調(diào)用派生類(lèi)的人們要問(wèn),能否用一個(gè)調(diào)用形式,既能調(diào)用派生類(lèi)的函數(shù),又能調(diào)用基類(lèi)同名函數(shù)?函數(shù),又能調(diào)用基類(lèi)同名函數(shù)?C+中的中的虛函數(shù)虛函數(shù)就是用來(lái)解就是用來(lái)解決這一問(wèn)題。決這一問(wèn)題。l虛函數(shù)的作用虛函數(shù)的作用:虛函數(shù)的作用是允許在

24、派生類(lèi)中重新定義與:虛函數(shù)的作用是允許在派生類(lèi)中重新定義與基類(lèi)同名的函數(shù),并且可以通過(guò)基類(lèi)指針或引用來(lái)訪問(wèn)基類(lèi)基類(lèi)同名的函數(shù),并且可以通過(guò)基類(lèi)指針或引用來(lái)訪問(wèn)基類(lèi)和派生類(lèi)中的同名函數(shù)。請(qǐng)看示例程序:和派生類(lèi)中的同名函數(shù)。請(qǐng)看示例程序:C+ 6- 18 示例示例#include #include using namespace std;class studentpublic: student (int n,string nam, float s) num= n; name= nam; score= s; void display( ) cout“num:”num “name:”name “sco

25、re:”scoreendl; protected: int num; string name; float score;class graduate : public studentpublic: graduate (int n, string nam, float s, float p): student (n,nam,s), pay(p) void display( ) cout“num:”num “name:”name “score:”score “pay:”paydisplay( ); / 指向基類(lèi)對(duì)象指向基類(lèi)對(duì)象s1 pt = &g1; pt-display( ); / 指向

26、派生類(lèi)對(duì)象指向派生類(lèi)對(duì)象g1,僅僅輸出了派生類(lèi)的基類(lèi)數(shù)據(jù)成員,因?yàn)檩敵隽伺缮?lèi)的基類(lèi)數(shù)據(jù)成員,因?yàn)樗{(diào)用的是基類(lèi)成員函數(shù)它調(diào)用的是基類(lèi)成員函數(shù)display! 假如想輸出派生類(lèi)的全部數(shù)據(jù),當(dāng)然可以采用下面兩種方法之一:假如想輸出派生類(lèi)的全部數(shù)據(jù),當(dāng)然可以采用下面兩種方法之一:n通過(guò)派生類(lèi)對(duì)象名通過(guò)派生類(lèi)對(duì)象名g1,調(diào)用派生類(lèi)對(duì)象的成員函數(shù):,調(diào)用派生類(lèi)對(duì)象的成員函數(shù):g1.display( );n定義一個(gè)指向派生類(lèi)的指針定義一個(gè)指向派生類(lèi)的指針ptr,并指向,并指向g1,然后用,然后用ptr-display( )。C+ 6- 19 虛函數(shù)虛函數(shù) 我們可以用虛函數(shù)可以順利解決這一問(wèn)題。方法是:我

27、們可以用虛函數(shù)可以順利解決這一問(wèn)題。方法是:u在基類(lèi)在基類(lèi)student 中聲明中聲明 display函數(shù)時(shí),在最左邊加上一個(gè)關(guān)鍵字函數(shù)時(shí),在最左邊加上一個(gè)關(guān)鍵字virtual :virtual void display( ); 就可以就可以student類(lèi)的類(lèi)的display函數(shù)聲明為虛函數(shù),程序其它部分不變編譯運(yùn)函數(shù)聲明為虛函數(shù),程序其它部分不變編譯運(yùn)行后,可見(jiàn),使用行后,可見(jiàn),使用pt-display( ),的確將,的確將graduate類(lèi)對(duì)象類(lèi)對(duì)象 g1 的全部數(shù)據(jù)顯示的全部數(shù)據(jù)顯示了出來(lái),說(shuō)明它調(diào)用的是了出來(lái),說(shuō)明它調(diào)用的是g1 的成員函數(shù)的成員函數(shù) display。u在派生類(lèi)中重新

28、定義該函數(shù),要求函數(shù)名、函數(shù)類(lèi)型、參數(shù)表完全相同。在派生類(lèi)中重新定義該函數(shù),要求函數(shù)名、函數(shù)類(lèi)型、參數(shù)表完全相同。u只在類(lèi)里的成員函數(shù)聲明時(shí),加上關(guān)鍵字只在類(lèi)里的成員函數(shù)聲明時(shí),加上關(guān)鍵字virtual,在類(lèi)外定義定義虛函數(shù)時(shí),在類(lèi)外定義定義虛函數(shù)時(shí),不必加不必加virtual關(guān)鍵字。關(guān)鍵字。u定義一個(gè)指向基類(lèi)對(duì)象的指針,并使她指向同一類(lèi)族中的某一對(duì)象;定義一個(gè)指向基類(lèi)對(duì)象的指針,并使她指向同一類(lèi)族中的某一對(duì)象;u通過(guò)該指針變量調(diào)用此虛函數(shù),此時(shí)調(diào)用的就是指針變量指向的對(duì)象的同名函通過(guò)該指針變量調(diào)用此虛函數(shù),此時(shí)調(diào)用的就是指針變量指向的對(duì)象的同名函數(shù)。數(shù)。C+ 6- 20 虛函數(shù)示例虛函數(shù)示例

29、通過(guò)使用虛函數(shù)和指針,就能方便地調(diào)用同一類(lèi)族中不同通過(guò)使用虛函數(shù)和指針,就能方便地調(diào)用同一類(lèi)族中不同類(lèi)對(duì)象的同名函數(shù),只要先用基類(lèi)指針指向該對(duì)象即可。類(lèi)對(duì)象的同名函數(shù),只要先用基類(lèi)指針指向該對(duì)象即可。#include #include using namespace std;class studentpublic: student (int n,string nam, float s) num= n; name= nam; score= s; virtual void display( ) cout“num:”num “name:”name “score:”scoreendl; protect

30、ed: int num; string name; float score;class graduate : public studentpublic: graduate (int n, string nam, float s, float p): student (n,nam,s), pay(p) void display( ) cout“num:”num “name:”name “score:”score “pay:”paydisplay( ); / 指向基類(lèi)對(duì)象指向基類(lèi)對(duì)象s1 pt = &g1; pt-display( ); / 指向派生類(lèi)對(duì)象指向派生類(lèi)對(duì)象g1,調(diào)調(diào)用用g1的

31、顯示函數(shù)的顯示函數(shù)display,輸出,輸出g1全部全部數(shù)據(jù)成員數(shù)據(jù)成員C+ 6- 21 虛函數(shù)示例虛函數(shù)示例例例9-4 用指針用指針+虛函數(shù)的形式實(shí)現(xiàn)動(dòng)態(tài)聯(lián)編。虛函數(shù)的形式實(shí)現(xiàn)動(dòng)態(tài)聯(lián)編。class B public: virtual f() ;class P: public B public: f() ;class Q: public B public: f() ;main () B* b_ptr; P p; Q q;b_ptr=&p;b_ptr-f(); /調(diào)用的是調(diào)用的是P:f()b_ptr=&q;b_ptr-f(); /調(diào)用的是調(diào)用的是Q:f()例例9-5 用引用用引用

32、+ 虛函數(shù)的形式實(shí)現(xiàn)動(dòng)態(tài)聯(lián)虛函數(shù)的形式實(shí)現(xiàn)動(dòng)態(tài)聯(lián)編。編。class B public: virtual f() ;class P: public B public: f() ;class Q: public B public: f() ;main () P p; Q q;B& b_ref1=p;b_ref.f(); /調(diào)用的是調(diào)用的是P:f()B& b_ref2=q;b_ref2.f(); /調(diào)用的是調(diào)用的是Q:f()C+ 6- 22 將函數(shù)重載與虛函數(shù)比較,可見(jiàn):將函數(shù)重載與虛函數(shù)比較,可見(jiàn):u函數(shù)重載是解決的是同一層次上的同名函數(shù)的問(wèn)題。是橫函數(shù)重載是解決的是同一層次上的同

33、名函數(shù)的問(wèn)題。是橫向重載。向重載。u虛函數(shù)解決的是不同派生層次上的同名函數(shù)的問(wèn)題。相當(dāng)虛函數(shù)解決的是不同派生層次上的同名函數(shù)的問(wèn)題。相當(dāng)于縱向重載。于縱向重載。u同一類(lèi)族的虛函數(shù)的首部是相同的;而重載函數(shù)的首部不同一類(lèi)族的虛函數(shù)的首部是相同的;而重載函數(shù)的首部不相同(參數(shù)表不能相同)。相同(參數(shù)表不能相同)。C+ 6- 23 虛函數(shù)何時(shí)為靜態(tài)聯(lián)編虛函數(shù)何時(shí)為靜態(tài)聯(lián)編?何時(shí)為動(dòng)態(tài)聯(lián)編?何時(shí)為動(dòng)態(tài)聯(lián)編?l靜態(tài)聯(lián)編與動(dòng)態(tài)聯(lián)編?kù)o態(tài)聯(lián)編與動(dòng)態(tài)聯(lián)編C+在編譯或運(yùn)行時(shí),對(duì)多個(gè)同名函數(shù)究竟調(diào)用哪一個(gè)函在編譯或運(yùn)行時(shí),對(duì)多個(gè)同名函數(shù)究竟調(diào)用哪一個(gè)函數(shù),需要一定的機(jī)制來(lái)確定。這種確定調(diào)用的具體對(duì)象的過(guò)程稱(chēng)數(shù),需要

34、一定的機(jī)制來(lái)確定。這種確定調(diào)用的具體對(duì)象的過(guò)程稱(chēng)為為“聯(lián)編聯(lián)編( binding,也稱(chēng)關(guān)聯(lián),也稱(chēng)關(guān)聯(lián) )”,即把函數(shù)名與某一個(gè)類(lèi)對(duì)象,即把函數(shù)名與某一個(gè)類(lèi)對(duì)象捆綁在一起。捆綁在一起。函數(shù)重載函數(shù)重載,在編譯時(shí)就可確定其調(diào)用的函數(shù)是哪一個(gè);,在編譯時(shí)就可確定其調(diào)用的函數(shù)是哪一個(gè);通通過(guò)對(duì)象名調(diào)用虛函數(shù)過(guò)對(duì)象名調(diào)用虛函數(shù),在編譯時(shí)也可確定其調(diào)用的虛函數(shù)屬于哪,在編譯時(shí)也可確定其調(diào)用的虛函數(shù)屬于哪一個(gè)類(lèi)。其過(guò)程稱(chēng)為一個(gè)類(lèi)。其過(guò)程稱(chēng)為“靜態(tài)關(guān)聯(lián)靜態(tài)關(guān)聯(lián)( static binding ),因?yàn)槭窃谶\(yùn),因?yàn)槭窃谶\(yùn)行前進(jìn)行關(guān)聯(lián)的,又成為行前進(jìn)行關(guān)聯(lián)的,又成為早期關(guān)聯(lián)早期關(guān)聯(lián)( early binding

35、)。C+ 6- 24 通過(guò)通過(guò)指針和虛函數(shù)的結(jié)合指針和虛函數(shù)的結(jié)合,在編譯階段是沒(méi)法進(jìn)行關(guān)聯(lián)的,在編譯階段是沒(méi)法進(jìn)行關(guān)聯(lián)的,因?yàn)榫幾g只作靜態(tài)的語(yǔ)法檢查,光從語(yǔ)句形式因?yàn)榫幾g只作靜態(tài)的語(yǔ)法檢查,光從語(yǔ)句形式pt-display( )無(wú)法無(wú)法確定調(diào)用的對(duì)象,也就沒(méi)法關(guān)聯(lián)。確定調(diào)用的對(duì)象,也就沒(méi)法關(guān)聯(lián)。出現(xiàn)這種情況,我們可以在運(yùn)行階段來(lái)處理關(guān)聯(lián)。在運(yùn)行階出現(xiàn)這種情況,我們可以在運(yùn)行階段來(lái)處理關(guān)聯(lián)。在運(yùn)行階段,基類(lèi)指針先指向某一個(gè)對(duì)象,然后通過(guò)指針調(diào)用該對(duì)象的成段,基類(lèi)指針先指向某一個(gè)對(duì)象,然后通過(guò)指針調(diào)用該對(duì)象的成員函數(shù)。此時(shí)調(diào)用哪個(gè)函數(shù)是確定的,沒(méi)有不確定因素。例如員函數(shù)。此時(shí)調(diào)用哪個(gè)函數(shù)是確定的

36、,沒(méi)有不確定因素。例如: pt = &g1; pt-display( ); 這種情況由于是在運(yùn)行階段將虛函數(shù)與某一類(lèi)對(duì)象這種情況由于是在運(yùn)行階段將虛函數(shù)與某一類(lèi)對(duì)象“綁定綁定”在一起的,因此稱(chēng)為在一起的,因此稱(chēng)為“動(dòng)態(tài)關(guān)聯(lián)動(dòng)態(tài)關(guān)聯(lián)( dynamic binding ),或,或“滯后滯后關(guān)聯(lián)關(guān)聯(lián)( late binding )”。C+ 6- 25 l使用虛函數(shù),要注意使用虛函數(shù),要注意u只能用只能用virtual 聲明類(lèi)的成員函數(shù),類(lèi)外的普通函數(shù)不能聲明聲明類(lèi)的成員函數(shù),類(lèi)外的普通函數(shù)不能聲明成虛函數(shù),因?yàn)樗鼪](méi)有繼承的操作。成虛函數(shù),因?yàn)樗鼪](méi)有繼承的操作。u一個(gè)成員函數(shù)被聲明成虛函數(shù)后,

37、在同一類(lèi)族中的類(lèi)就不能一個(gè)成員函數(shù)被聲明成虛函數(shù)后,在同一類(lèi)族中的類(lèi)就不能再定義一個(gè)非再定義一個(gè)非virtual的、但與該函數(shù)具有相同參數(shù)表和返回的、但與該函數(shù)具有相同參數(shù)表和返回類(lèi)型的同名函數(shù)。類(lèi)型的同名函數(shù)。u使用虛函數(shù),系統(tǒng)要有一定的空間開(kāi)銷(xiāo)。當(dāng)一個(gè)類(lèi)帶有虛函使用虛函數(shù),系統(tǒng)要有一定的空間開(kāi)銷(xiāo)。當(dāng)一個(gè)類(lèi)帶有虛函數(shù)時(shí),編譯系統(tǒng)會(huì)為該類(lèi)構(gòu)造一個(gè)虛函數(shù)表數(shù)時(shí),編譯系統(tǒng)會(huì)為該類(lèi)構(gòu)造一個(gè)虛函數(shù)表( virtual function table, vtable ),它是一個(gè)指針數(shù)組,存放每個(gè)虛函數(shù)的入口,它是一個(gè)指針數(shù)組,存放每個(gè)虛函數(shù)的入口地址。地址。C+ 6- 26 l什么情況下使用虛函數(shù)?什么

38、情況下使用虛函數(shù)?u成員函數(shù)所在的類(lèi)是否會(huì)作為基類(lèi)?成員函數(shù)被繼承后有成員函數(shù)所在的類(lèi)是否會(huì)作為基類(lèi)?成員函數(shù)被繼承后有沒(méi)有可能發(fā)生功能變化,如果兩個(gè)因素都具備,就應(yīng)該將沒(méi)有可能發(fā)生功能變化,如果兩個(gè)因素都具備,就應(yīng)該將它聲明成虛函數(shù)。它聲明成虛函數(shù)。u如果成員函數(shù)被繼承后功能不變,或派生類(lèi)用不到該函數(shù),如果成員函數(shù)被繼承后功能不變,或派生類(lèi)用不到該函數(shù),就不要聲明成虛函數(shù)。就不要聲明成虛函數(shù)。u應(yīng)考慮對(duì)成員函數(shù)的訪問(wèn)是通過(guò)對(duì)象名還是基類(lèi)指針,如應(yīng)考慮對(duì)成員函數(shù)的訪問(wèn)是通過(guò)對(duì)象名還是基類(lèi)指針,如果是通過(guò)果是通過(guò)基類(lèi)指針基類(lèi)指針或或引用引用訪問(wèn),則應(yīng)當(dāng)聲明為虛函數(shù)。訪問(wèn),則應(yīng)當(dāng)聲明為虛函數(shù)。u有

39、時(shí)基類(lèi)中定義虛函數(shù)時(shí)并不定義它的函數(shù)體,即函數(shù)體有時(shí)基類(lèi)中定義虛函數(shù)時(shí)并不定義它的函數(shù)體,即函數(shù)體為空。其作用只是定義了一個(gè)虛函數(shù)名,具體功能留給派為空。其作用只是定義了一個(gè)虛函數(shù)名,具體功能留給派生類(lèi)添加(生類(lèi)添加(純虛函數(shù)純虛函數(shù))。)。C+ 6- 27 l虛析構(gòu)函數(shù)虛析構(gòu)函數(shù)u問(wèn)題的引出:我們知道,當(dāng)派生類(lèi)對(duì)象撤消時(shí),系統(tǒng)先調(diào)用派生類(lèi)析問(wèn)題的引出:我們知道,當(dāng)派生類(lèi)對(duì)象撤消時(shí),系統(tǒng)先調(diào)用派生類(lèi)析構(gòu)函數(shù),再調(diào)用基類(lèi)析構(gòu)函數(shù)。但是,如果用構(gòu)函數(shù),再調(diào)用基類(lèi)析構(gòu)函數(shù)。但是,如果用new 運(yùn)算符建立了一運(yùn)算符建立了一個(gè)派生類(lèi)臨時(shí)對(duì)象,但用一個(gè)基類(lèi)指針指向它,當(dāng)程序用帶指針參數(shù)個(gè)派生類(lèi)臨時(shí)對(duì)象,但

40、用一個(gè)基類(lèi)指針指向它,當(dāng)程序用帶指針參數(shù)的的delete 撤消對(duì)象時(shí),會(huì)發(fā)生讓人不能接受的情況:系統(tǒng)只析構(gòu)基類(lèi)撤消對(duì)象時(shí),會(huì)發(fā)生讓人不能接受的情況:系統(tǒng)只析構(gòu)基類(lèi)對(duì)象,而不析構(gòu)派生類(lèi)對(duì)象(即只會(huì)調(diào)用基類(lèi)的析構(gòu)函數(shù),而不會(huì)調(diào)對(duì)象,而不析構(gòu)派生類(lèi)對(duì)象(即只會(huì)調(diào)用基類(lèi)的析構(gòu)函數(shù),而不會(huì)調(diào)用派生類(lèi)的析構(gòu)函數(shù),這就使得派生類(lèi)無(wú)法執(zhí)行某些清理工作):用派生類(lèi)的析構(gòu)函數(shù),這就使得派生類(lèi)無(wú)法執(zhí)行某些清理工作):class pointpublic: point( ) point( ) cout“析構(gòu)基類(lèi)對(duì)象析構(gòu)基類(lèi)對(duì)象”endl; ;class circle : public pointpublic: circ

41、le( ) circle ( ) cout“析構(gòu)派生類(lèi)對(duì)象析構(gòu)派生類(lèi)對(duì)象”endl; private: int radius;int main( ) point *p = new circle; / 指針為指向基類(lèi)對(duì)象指針,指針為指向基類(lèi)對(duì)象指針, / 但實(shí)際指向臨時(shí)派生類(lèi)對(duì)象但實(shí)際指向臨時(shí)派生類(lèi)對(duì)象 delete p; return 0;C+ 6- 28 解決的辦法:解決的辦法:可以將基類(lèi)的析構(gòu)函數(shù)聲明為虛析構(gòu)函數(shù)。如:可以將基類(lèi)的析構(gòu)函數(shù)聲明為虛析構(gòu)函數(shù)。如: virtual point( ) cout“析構(gòu)基類(lèi)對(duì)象析構(gòu)基類(lèi)對(duì)象”endl; 程序其它部分不動(dòng),就行了。程序其它部分不動(dòng),就行

42、了。當(dāng)基類(lèi)的析構(gòu)函數(shù)被定義成當(dāng)基類(lèi)的析構(gòu)函數(shù)被定義成virtual,無(wú)論指針指向同,無(wú)論指針指向同一類(lèi)族中的哪一個(gè)對(duì)象,當(dāng)撤消對(duì)象時(shí),系統(tǒng)會(huì)采用動(dòng)態(tài)關(guān)一類(lèi)族中的哪一個(gè)對(duì)象,當(dāng)撤消對(duì)象時(shí),系統(tǒng)會(huì)采用動(dòng)態(tài)關(guān)聯(lián),調(diào)用相應(yīng)的析構(gòu)函數(shù),清理該對(duì)象,然后再析構(gòu)基類(lèi)對(duì)聯(lián),調(diào)用相應(yīng)的析構(gòu)函數(shù),清理該對(duì)象,然后再析構(gòu)基類(lèi)對(duì)象。象。C+ 6- 29 其他需要注意的問(wèn)題:其他需要注意的問(wèn)題:要實(shí)現(xiàn)運(yùn)行時(shí)的多態(tài),需要以下條件:l(1)必須通過(guò)指向基類(lèi)對(duì)象的指針,訪問(wèn)和基類(lèi)成員函數(shù)同名的派生類(lèi)成員函數(shù)。或者用派生類(lèi)對(duì)象初始化的基類(lèi)對(duì)象的引用,訪問(wèn)和基類(lèi)成員函數(shù)同名的派生類(lèi)成員函數(shù)。l(2)派生類(lèi)的繼承方式必須是公有繼承

43、。l(3)基類(lèi)中的同名成員函數(shù)必須定義為虛函數(shù)。C+ 6- 30 其他需要注意的問(wèn)題:其他需要注意的問(wèn)題:使用虛函數(shù)時(shí)要遵循以下規(guī)則:l(1)必須首先在基類(lèi)中聲明虛函數(shù)。在多級(jí)繼承的情況下,也可以不在最高層的基類(lèi)中聲明虛函數(shù)。例如在第二層定義的虛函數(shù),可以和第三層的虛函數(shù)形成動(dòng)態(tài)聯(lián)編。但是,一般都是在最高層的基類(lèi)中首先聲明虛函數(shù)。l(2)基類(lèi)和派生類(lèi)的同名函數(shù),函數(shù)名、返回值、參數(shù)表必須全部相同,才能作為虛函數(shù)來(lái)使用。否則,即使函數(shù)用virtual 來(lái)說(shuō)明,也不具有虛函數(shù)的行為。l(3)靜態(tài)成員函數(shù)不可以聲明為虛函數(shù)。構(gòu)造函數(shù)也不可以聲明為虛函數(shù)。l(4)析構(gòu)函數(shù)可以聲明為虛函數(shù),即可以定義虛

44、析構(gòu)函數(shù)。l(5)如果在多層繼承中,最高層和第三層有兩個(gè)原型相同的函數(shù),并在最高層中聲明為虛函數(shù),則第三層的這個(gè)函數(shù)也是虛函數(shù)。這種關(guān)系不會(huì)因?yàn)榈诙記](méi)有定義這個(gè)函數(shù)而受到影響。例9-7。C+ 6- 31 9.4 純虛函數(shù)與抽象類(lèi)純虛函數(shù)與抽象類(lèi)前面已經(jīng)提到,有時(shí)在基類(lèi)中將某一成員函數(shù)定為虛函前面已經(jīng)提到,有時(shí)在基類(lèi)中將某一成員函數(shù)定為虛函數(shù)并不是基類(lèi)本身的需要,而是派生類(lèi)的需要。在基類(lèi)中預(yù)留數(shù)并不是基類(lèi)本身的需要,而是派生類(lèi)的需要。在基類(lèi)中預(yù)留一個(gè)函數(shù)名,具體功能留給派生類(lèi)根據(jù)需要去定義。一個(gè)函數(shù)名,具體功能留給派生類(lèi)根據(jù)需要去定義。在上一節(jié)中基類(lèi)在上一節(jié)中基類(lèi)point 中沒(méi)有定義面積中沒(méi)

45、有定義面積area函數(shù),是因函數(shù),是因?yàn)闉椤包c(diǎn)點(diǎn)”沒(méi)有面積的概念。但是,其直接派生類(lèi)沒(méi)有面積的概念。但是,其直接派生類(lèi)circle和間接和間接派生類(lèi)派生類(lèi)cylinder卻都需要卻都需要area 函數(shù),而且這兩個(gè)函數(shù),而且這兩個(gè)area 函數(shù)的函數(shù)的功能還不相同,一個(gè)是求圓面積,一個(gè)是求圓柱體表面積。功能還不相同,一個(gè)是求圓面積,一個(gè)是求圓柱體表面積。也許會(huì)想到,在基類(lèi)也許會(huì)想到,在基類(lèi)point 中加一個(gè)中加一個(gè)area 函數(shù),并聲明函數(shù),并聲明為虛函數(shù):為虛函數(shù):virtual float area ( ) const return 0; 其返回值為其返回值為0表示表示“點(diǎn)點(diǎn)”沒(méi)有面積。其

46、實(shí),在基類(lèi)中并不沒(méi)有面積。其實(shí),在基類(lèi)中并不使用這個(gè)函數(shù),其返回值也沒(méi)有意義。使用這個(gè)函數(shù),其返回值也沒(méi)有意義。C+ 6- 32 9.4 純虛函數(shù)與抽象類(lèi)純虛函數(shù)與抽象類(lèi)為簡(jiǎn)化起見(jiàn),可以不寫(xiě)出這種無(wú)意義的函數(shù)體,只給出為簡(jiǎn)化起見(jiàn),可以不寫(xiě)出這種無(wú)意義的函數(shù)體,只給出函數(shù)的原型,并在后面加上函數(shù)的原型,并在后面加上“=0”,如:,如:virtual float area( ) const =0 ; / 純虛函數(shù)純虛函數(shù)這就將這就將area聲明為一個(gè)純虛函數(shù)聲明為一個(gè)純虛函數(shù)(pure virtual function)l純虛函數(shù)的聲明形式純虛函數(shù)的聲明形式virtual 函數(shù)類(lèi)型函數(shù)類(lèi)型 函數(shù)名

47、函數(shù)名( 參數(shù)表參數(shù)表 ) =0;說(shuō)明說(shuō)明u純虛函數(shù)沒(méi)有函數(shù)體;純虛函數(shù)沒(méi)有函數(shù)體;u最后的最后的“=0”不表示函數(shù)值返回不表示函數(shù)值返回0,它只是形式上的作用,它只是形式上的作用,告訴編譯系統(tǒng):這是純虛函數(shù),告訴編譯系統(tǒng):這是純虛函數(shù),u這是一個(gè)聲明語(yǔ)句,以分號(hào)結(jié)尾。這是一個(gè)聲明語(yǔ)句,以分號(hào)結(jié)尾。u如果基類(lèi)中聲明了純虛函數(shù),但派生類(lèi)中沒(méi)有定義該函數(shù),如果基類(lèi)中聲明了純虛函數(shù),但派生類(lèi)中沒(méi)有定義該函數(shù),則該函數(shù)在派生類(lèi)中仍為純虛函數(shù)。則該函數(shù)在派生類(lèi)中仍為純虛函數(shù)。C+ 6- 33 9.4 純虛函數(shù)與抽象類(lèi)純虛函數(shù)與抽象類(lèi)l抽象類(lèi)抽象類(lèi)u什么叫抽象類(lèi)?什么叫抽象類(lèi)?一般聲明了一個(gè)類(lèi),用來(lái)定義若

48、干對(duì)象。一般聲明了一個(gè)類(lèi),用來(lái)定義若干對(duì)象。但有些類(lèi)并不是用來(lái)生成對(duì)象,而是作為基類(lèi)去建立派生但有些類(lèi)并不是用來(lái)生成對(duì)象,而是作為基類(lèi)去建立派生類(lèi)。類(lèi)。這種不用來(lái)定義對(duì)象,而只作為一種基本類(lèi)型用做繼這種不用來(lái)定義對(duì)象,而只作為一種基本類(lèi)型用做繼承的類(lèi),就叫抽象類(lèi)承的類(lèi),就叫抽象類(lèi)( abstract class )。由于抽象類(lèi)常作。由于抽象類(lèi)常作為基類(lèi),我們也稱(chēng)為抽象基類(lèi)為基類(lèi),我們也稱(chēng)為抽象基類(lèi)( abstract base class )。比如,凡是包含純虛函數(shù)的類(lèi)都叫抽象類(lèi)。因?yàn)榧兲摫热?,凡是包含純虛函?shù)的類(lèi)都叫抽象類(lèi)。因?yàn)榧兲摵瘮?shù)不能被調(diào)用,包含純虛函數(shù)的類(lèi)無(wú)法建立對(duì)象。函數(shù)不能被調(diào)用

49、,包含純虛函數(shù)的類(lèi)無(wú)法建立對(duì)象。u抽象類(lèi)的作用抽象類(lèi)的作用:是作為一個(gè)類(lèi)族的共同基類(lèi)。即,為一個(gè):是作為一個(gè)類(lèi)族的共同基類(lèi)。即,為一個(gè)類(lèi)族提供一個(gè)公共接口。類(lèi)族提供一個(gè)公共接口。一個(gè)類(lèi)層次結(jié)構(gòu)中可以不包含任何抽象類(lèi),每一層次一個(gè)類(lèi)層次結(jié)構(gòu)中可以不包含任何抽象類(lèi),每一層次的類(lèi)都可以建立對(duì)象。但是,許多系統(tǒng)的頂層是一個(gè)抽象的類(lèi)都可以建立對(duì)象。但是,許多系統(tǒng)的頂層是一個(gè)抽象類(lèi),甚至頂部有好幾層都是抽象類(lèi)。類(lèi),甚至頂部有好幾層都是抽象類(lèi)。C+ 6- 34 9.4 純虛函數(shù)與抽象類(lèi)純虛函數(shù)與抽象類(lèi)如果由抽象類(lèi)所派生出的新類(lèi)中對(duì)基類(lèi)的所有純虛函數(shù)如果由抽象類(lèi)所派生出的新類(lèi)中對(duì)基類(lèi)的所有純虛函數(shù)都進(jìn)行了定義

50、,這個(gè)派生類(lèi)就不是抽象類(lèi),可以被調(diào)用,這個(gè)都進(jìn)行了定義,這個(gè)派生類(lèi)就不是抽象類(lèi),可以被調(diào)用,這個(gè)派生類(lèi)就成為可以用來(lái)定義對(duì)象的派生類(lèi)就成為可以用來(lái)定義對(duì)象的具體類(lèi)具體類(lèi)( concrete class )。如果由抽象類(lèi)所派生出的新類(lèi)中對(duì)基類(lèi)的純虛函數(shù)沒(méi)有如果由抽象類(lèi)所派生出的新類(lèi)中對(duì)基類(lèi)的純虛函數(shù)沒(méi)有都進(jìn)行定義,這個(gè)派生類(lèi)就仍然是抽象類(lèi)。都進(jìn)行定義,這個(gè)派生類(lèi)就仍然是抽象類(lèi)。雖然抽象類(lèi)不能定義對(duì)象,但可以定義指向抽象類(lèi)的數(shù)雖然抽象類(lèi)不能定義對(duì)象,但可以定義指向抽象類(lèi)的數(shù)據(jù)的指針。當(dāng)派生類(lèi)成為具體類(lèi)之后,就可以用這種指針指向據(jù)的指針。當(dāng)派生類(lèi)成為具體類(lèi)之后,就可以用這種指針指向派生類(lèi)對(duì)象,然后通

51、過(guò)該指針調(diào)用虛函數(shù),實(shí)現(xiàn)多態(tài)性操作。派生類(lèi)對(duì)象,然后通過(guò)該指針調(diào)用虛函數(shù),實(shí)現(xiàn)多態(tài)性操作。下面請(qǐng)看一個(gè)示例,對(duì)之前介紹過(guò)的以下面請(qǐng)看一個(gè)示例,對(duì)之前介紹過(guò)的以point為基類(lèi)的點(diǎn)為基類(lèi)的點(diǎn)-圓圓-圓柱體類(lèi)的層次結(jié)構(gòu)進(jìn)行改寫(xiě),類(lèi)的頂層是抽象基類(lèi)圓柱體類(lèi)的層次結(jié)構(gòu)進(jìn)行改寫(xiě),類(lèi)的頂層是抽象基類(lèi) shape,點(diǎn)、圓、圓柱體都是,點(diǎn)、圓、圓柱體都是shape的直接或間接派生類(lèi)。的直接或間接派生類(lèi)。C+ 6- 35 #include class shapepublic: virtual float area( ) const return 0.0; / 虛函數(shù)虛函數(shù) virtual float volum

52、e( ) const return 0.0; / 虛函數(shù)虛函數(shù) virtual void shapeName( ) const =0; / 純虛函數(shù)純虛函數(shù); / 抽象類(lèi)不能也不必定義對(duì)象抽象類(lèi)不能也不必定義對(duì)象class point : public shapepublic: point (float =0, float =0); void setPoint (float, float); float getX( ) return x; float getY( ) return y; virtual void shapeName ( ) const cout“point:”; / 對(duì)虛函數(shù)再

53、定義對(duì)虛函數(shù)再定義 friend ostream &operator ( ostream &, const point &);protected: float x,y;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“p.x“,” p.y“”; return output;C+ 6

54、- 36 class circle : public pointpublic: circle (float x=0, float y=0, float r=0); float getRadius( ) const; virtual float area ( ) const; / 因?yàn)榛?lèi)此因?yàn)榛?lèi)此函數(shù)定義為虛函數(shù),所以在這里,不管有沒(méi)函數(shù)定義為虛函數(shù),所以在這里,不管有沒(méi)有有virtual, 只要函數(shù)名、參數(shù)表相同,都是只要函數(shù)名、參數(shù)表相同,都是虛函數(shù)虛函數(shù) virtual void shapeName ( ) const cout“circle:”; / 對(duì)虛函數(shù)再定義對(duì)虛函數(shù)再定義 f

55、riend ostream &operator( ostream &, const circle &);protected: float radius;/ 定義定義circle類(lèi)成員函數(shù)類(lèi)成員函數(shù)circle : circle (float x, float y, float r) : point (x,y),radius ( r ) float circle : getRadius ( ) const return radius; float circle : area( ) const return 3.14159 * radius * radius; / 重新定義

56、了重新定義了area 函數(shù)函數(shù)ostream &operator ( ostream &output, const circle &c)output“c.x“,” c.y“, r=”c.radius; return output;/ volume 對(duì)于對(duì)于point、circle類(lèi)沒(méi)有意義,類(lèi)沒(méi)有意義,只是簡(jiǎn)單繼承過(guò)來(lái),而沒(méi)有重新定義只是簡(jiǎn)單繼承過(guò)來(lái),而沒(méi)有重新定義C+ 6- 37 class cylinder : public circlepublic: cylinder ( float x=0, float y=0, float r=0, float h=0 );

57、void setHeight (float); virtual float area ( ) const; virtual float volume ( ) const; virtual void shapeName ( ) const cout“cylinder:”; / 對(duì)虛函數(shù)再定義對(duì)虛函數(shù)再定義 friend ostream &operator( ostream &, const cylinder &);protected: float height;/ 定義定義cylinder類(lèi)成員函數(shù)類(lèi)成員函數(shù)cylinder : cylinder ( float a, f

58、loat b, float r, float 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;/ 重新定義了重新定義了area 函數(shù)函數(shù)float cylinder : volume ( ) const return circle : area( ) * height; / 重新定義了重新定義了volume 函數(shù)函數(shù)os

59、tream &operator ( ostream &output, const cylinder &cy)output“cy.x“,” cy.y“, r=”cy.radius “,h=“cy.height; return output;C+ 6- 38 void main ( ) point p1(3.2, 4.5); circle c1(2.4, 1.2, 5.6); cylinder cy1(3.5, 6.4, 5.2, 10.5); p1.shapeName( ); / 靜態(tài)關(guān)聯(lián)靜態(tài)關(guān)聯(lián) cout p1 endl; c1.shapeName( ); / 靜態(tài)關(guān)聯(lián)

60、靜態(tài)關(guān)聯(lián) cout c1 endl; cy1.shapeName( ); / 靜態(tài)關(guān)聯(lián)靜態(tài)關(guān)聯(lián) cout cy1 shapeName ( ); / 動(dòng)態(tài)關(guān)聯(lián)動(dòng)態(tài)關(guān)聯(lián) cout“x=“p1.getX( ) “,y=“p1.getY( ) “narea=“area( ) “nvolume=“ volume( )shapeName( ); / 動(dòng)態(tài)關(guān)聯(lián)動(dòng)態(tài)關(guān)聯(lián)cout“x=“c1.getX( )“,y=“ c1.getY( )“narea=“ area( )“nvolume=“ volume( )shapeName( ); / 動(dòng)態(tài)關(guān)聯(lián)動(dòng)態(tài)關(guān)聯(lián)cout“x=“cy1.getX( ) “,y=“cy1.getY( ) “narea=“area( ) “nvolume=“ volume( )shapeName( ), 就是動(dòng)態(tài)關(guān)聯(lián)。就是動(dòng)態(tài)關(guān)聯(lián)。C+ 6- 39 C+ 6- 40 多態(tài)性把操作細(xì)節(jié)留給類(lèi)的設(shè)計(jì)者(專(zhuān)業(yè)人員)去完多態(tài)性把操作細(xì)節(jié)留給類(lèi)的設(shè)計(jì)者(專(zhuān)業(yè)人員)去完成,而讓編程人員(類(lèi)的使用者)只需做一些宏觀性的工作,成,而讓編程人員(類(lèi)的使用者)只需做一些宏觀性的工作,告訴系統(tǒng)做什么,不必考慮怎么做,簡(jiǎ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)論