C++程序設(shè)計(jì):第7章 繼承_第1頁(yè)
C++程序設(shè)計(jì):第7章 繼承_第2頁(yè)
C++程序設(shè)計(jì):第7章 繼承_第3頁(yè)
C++程序設(shè)計(jì):第7章 繼承_第4頁(yè)
C++程序設(shè)計(jì):第7章 繼承_第5頁(yè)
已閱讀5頁(yè),還剩92頁(yè)未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

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

文檔簡(jiǎn)介

1、第 七 章 繼 承第七章 繼承第 七 章 繼 承 繼承是C+語(yǔ)言的一個(gè)重要特點(diǎn),它不僅簡(jiǎn)化了子類(lèi)的定義,而且利用繼承還可以達(dá)到軟件復(fù)用的目的,同時(shí)可以提高軟件的可靠性和縮短軟件開(kāi)發(fā)周期,提高軟件開(kāi)發(fā)效率。7.1 基類(lèi)和派生類(lèi) 在C+語(yǔ)言中,通過(guò)使用已有的類(lèi)并在此基礎(chǔ)上追加新的功能就可以派生出新的類(lèi),這一處理過(guò)程被稱為繼承。 被繼承的類(lèi)稱為基類(lèi),通過(guò)繼承而產(chǎn)生的新類(lèi)被稱為派生類(lèi)或?qū)С鲱?lèi)。通?;?lèi)又被稱為父類(lèi),派生類(lèi)又被稱為子類(lèi)。派生類(lèi)繼承了基類(lèi)的功能。【例7.1】編一程序,用于說(shuō)明派生類(lèi)的生成和使用 #includeclass cBase int va; int vb;public: cBase

2、( ) va=0; vb=0; cBase(int a, int b) va=a; vb=b; void set_va(int dt) va=dt; void set_vb(int dt) vb=dt; int get_va( ) return va; int get_vb( ) return vb; void disp( );void cBase:disp( ) cout “va=” va n; cout “vb=” vb n;class cDerived: public cBase int vc;public: cDerived( ); cDerived( int a, int b, in

3、t c ); void set_vc(int dt) vc=dt; int get_vc( ) return vc; void disp( );cDerived:cDerived( ): cBase( ) vc=0;cDerived:cDerived(int a, int b, int c): cBase(a, b) vc=c;void cDerived:disp( ) /cout “va=” va n; /不能直接使用 /cout “vb=” vb n; /不能直接使用 cBase:disp( ); cout “vc=” vc n;void main( ) cout “cBase class

4、:n”; cBase a(10, 20); a.disp( ); cout “cDerived class:n”; cDerived b(30, 40, 50); b.disp( ); b.set_va(60); b.set_vb(70); b.set_vc(80); b.disp( );執(zhí)行結(jié)果: cBase class: va=10 vb=20 cDerived class: va=30 vb=40 vc=50 va=60 vb=70 vc=80說(shuō) 明 在派生類(lèi)中可以直接使用基類(lèi)中所有的public和protected成員,但不能直接使用其private成員,這就是為什么不能在派生類(lèi)的di

5、sp( )函數(shù)中直接顯示va和vb的原因。有關(guān)派生類(lèi)的知識(shí):class 派生類(lèi)名: public 基類(lèi)名 追加的數(shù)據(jù)成員 追加的成員函數(shù);(1)派生類(lèi)的寫(xiě)法:位于冒號(hào)后面的public,表示當(dāng)派生類(lèi)繼承了基類(lèi)以后,在派生類(lèi)中對(duì)基類(lèi)成員的訪問(wèn)控制,即繼承方式。(2)對(duì)基類(lèi)的訪問(wèn)控制public(公有繼承)protected(保護(hù)繼承)private(私有繼承) 對(duì)基類(lèi)的訪問(wèn)控制與繼承方式有關(guān),在C+語(yǔ)言中共有3種繼承方式:a. 當(dāng)對(duì)基類(lèi)的訪問(wèn)控制為public時(shí) 基 類(lèi) 派 生 類(lèi)public 成員protected 成員private 成員public 處理protected 處理不可訪問(wèn) b

6、. 當(dāng)對(duì)基類(lèi)的訪問(wèn)控制為protected時(shí)基 類(lèi) 派 生 類(lèi)public 成員protected 成員private 成員protected處理protected 處理不可訪問(wèn) c. 當(dāng)對(duì)基類(lèi)的訪問(wèn)控制為private時(shí)基 類(lèi) 派 生 類(lèi)public 成員protected 成員private 成員private處理private處理不可訪問(wèn)(3)在派生類(lèi)的構(gòu)造函數(shù)中可以使用基類(lèi)的構(gòu)造函數(shù) 通過(guò)繼承操作所生成的派生類(lèi)可以有自己的構(gòu)造函數(shù),為了能夠在派生類(lèi)的構(gòu)造函數(shù)中為基類(lèi)中的數(shù)據(jù)成員進(jìn)行初始化,可以在派生類(lèi)的構(gòu)造函數(shù)中來(lái)調(diào)用基類(lèi)中的構(gòu)造函數(shù)。例如 cDerived:cDerived(int

7、a, int b, int c) : cBase(a, b) vc=c; 此函數(shù)是cDerived類(lèi)的構(gòu)造函數(shù),冒號(hào)后面的寫(xiě)法表示在該派生類(lèi)的構(gòu)造函數(shù)中要調(diào)用基類(lèi)的構(gòu)造函數(shù)。其處理過(guò)程是:首先調(diào)用基類(lèi)的構(gòu)造函數(shù)來(lái)初始化基類(lèi)的數(shù)據(jù)成員,然后再對(duì)派生類(lèi)中追加的數(shù)據(jù)成員進(jìn)行初始化。注意: 如果在派生類(lèi)的構(gòu)造函數(shù)中沒(méi)有顯式地調(diào)用基類(lèi)的構(gòu)造函數(shù)的話,則將隱式地調(diào)用基類(lèi)的缺省構(gòu)造函數(shù)。這時(shí),如果在基類(lèi)中沒(méi)有定義缺省構(gòu)造函數(shù)將出錯(cuò)。(4)在派生類(lèi)的構(gòu)造函數(shù)和成員函數(shù)中,可以調(diào)用基類(lèi)中的非private成員函數(shù) 由于派生類(lèi)繼承了基類(lèi)中的非private成員(包括數(shù)據(jù)成員和成員函數(shù)),因此在派生類(lèi)中可以調(diào)用基類(lèi)

8、中的非private成員函數(shù)。例如cDerived:cDerived( ) set_va(0); set_vb(0); vc=0;cDerived:cDerived( ):cBase( ) vc=0;不使用基類(lèi)構(gòu)造函數(shù),將其改寫(xiě): set_va(0)和set_vb(0)都是調(diào)用基類(lèi)中的函數(shù),在派生類(lèi)中可以使用基類(lèi)中的成員函數(shù)或數(shù)據(jù)成員,只要滿足訪問(wèn)權(quán)限要求即可。 (5)在派生類(lèi)中不能使用基類(lèi)中的private成員 前面已經(jīng)多次指出,在派生類(lèi)中只能使用基類(lèi)中的public成員和protected成員,而不能使用基類(lèi)的private成員。例如cDerived:cDerived( ) va=0; /

9、出錯(cuò) vb=0; /出錯(cuò) vc=0; 由于va和vb是基類(lèi)中private數(shù)據(jù)成員,因此,在派生類(lèi)的函數(shù)中不能直接對(duì)其進(jìn)行訪問(wèn)的。只能通過(guò)基類(lèi)中的非private成員函數(shù)來(lái)訪問(wèn)。(6)如果在基類(lèi)和派生類(lèi)中有同名函數(shù),則在派生類(lèi)中來(lái)使用基類(lèi)中的同名函數(shù)時(shí),需要利用作用域限定運(yùn)算符來(lái)加以區(qū)分 void cDerived:disp( ) cBase:disp( ); cout “vc=” vc n;調(diào)用的disp( )函數(shù)就是基類(lèi)中的disp( )函數(shù),而不是派生類(lèi)中的disp( )函數(shù),如果在派生類(lèi)中沒(méi)有定義disp( )函數(shù)的話,則cBase:可以省略。 (7)在派生類(lèi)中可以使用基類(lèi)和派生類(lèi)中的

10、成員函數(shù) 通過(guò)繼承操作所生成的派生類(lèi)中的成員函數(shù)可以使用基類(lèi)中的非private成員函數(shù)以及派生類(lèi)中的所有成員函數(shù)。例 如:cDerived b(30,40,50); /定義派生類(lèi)對(duì)象b.disp( ); /調(diào)用派生類(lèi)中新追加的disp( )函數(shù)b.set_va(60); /調(diào)用基類(lèi)中的函數(shù)b.set_vb(70); /調(diào)用基類(lèi)中的函數(shù)b.set_vc(80); /調(diào)用派生類(lèi)中所增加的函數(shù)b.disp( ); /調(diào)用派生類(lèi)中所增加的函數(shù)結(jié)論: 從上面的程序段可以看出,在派生類(lèi)中可以使用能夠被派生類(lèi)所繼承的基類(lèi)中的所有函數(shù),包括基類(lèi)中的public成員和protected成員。7.2 虛函數(shù)和多

11、態(tài) 一般來(lái)講,多態(tài)是指同一種東西有多種形態(tài)。在C+語(yǔ)言中,所謂多態(tài)就是指一個(gè)名字代表多種具體的對(duì)象。 在C+語(yǔ)言中,多態(tài)是通過(guò)虛函數(shù)來(lái)實(shí)現(xiàn)的。多態(tài)的概念比較抽象,單純用文字來(lái)敘述的話,難理解。我們將通過(guò)易于理解的例子來(lái)介紹多態(tài)的實(shí)現(xiàn)和使用。在介紹多態(tài)之前,先介紹一下有關(guān)動(dòng)態(tài)結(jié)合和靜態(tài)結(jié)合的概念。 7.2.1 靜態(tài)結(jié)合和動(dòng)態(tài)結(jié)合 為了說(shuō)明靜態(tài)結(jié)合與動(dòng)態(tài)結(jié)合的概念,請(qǐng)分析下面的例子?!纠?.2】編一程序,用于說(shuō)明靜態(tài)結(jié)合與動(dòng)態(tài)結(jié)合的概念。 #include class cBase int va; int vb; public: cBase( ) va=0; vb=0; cBase(int a, i

12、nt b) va=a; vb=b; void set_va(int dt) va=dt; void set_vb(int dt) vb=dt; int get_va( ) return va; int get_vb( ) return vb; void disp( ); void cBase:disp( ) cout “va=” va n; cout “vb=” vb n;class cDerived: public cBase int vc;public: cDerived( ); cDerived( int a, int b, int c ); void set_vc(int dt) vc

13、=dt; int get_vc( ) return vc; void disp( );cDerived:cDerived( ): cBase( ) vc=0;cDerived:cDerived(int a, int b, int c) :cBase(a, b) vc=c;void cDerived:disp( ) /cout “va=” va n; /不能直接使用 /cout “vb=” vb n; /不能直接使用cBase:disp( ); cout “vc=” vc disp( ); /使用指針來(lái)調(diào)用cBase的disp( ) p=&dt3; /設(shè)置cDerived對(duì)象的地址 p-disp

14、( ); /打算使用指針來(lái)調(diào)用cDerived的disp( ) 執(zhí)行結(jié)果:va=10 vb=20va=300 vb=400vc=500va=10 vb=20va=300vb=400注意main( )函數(shù)中的最后兩行: 說(shuō) 明p=&dt3; /p被定義為指向基類(lèi)的指針p-disp( );指針p被設(shè)置為派生類(lèi)對(duì)象dt3的地址,利用p來(lái)調(diào)用disp( )函數(shù),則一定是調(diào)用dt3對(duì)象中所繼承的基類(lèi)中的disp( )函數(shù), 表面上看來(lái)是調(diào)用dt3.disp( )函數(shù),顯示當(dāng)前對(duì)象中的va、vb和vc三個(gè)數(shù),而實(shí)際上是調(diào)用dt3對(duì)象中所繼承的基類(lèi)中的disp( )函數(shù),也就是只顯示va和vb兩個(gè)數(shù),這是因

15、為p被定義為指向基類(lèi)cBase對(duì)象的指針。 cBase *p;即: 因此,利用指針p來(lái)調(diào)用的disp( )函數(shù),一定是基類(lèi)中的disp( )函數(shù),這是由于在編譯時(shí)就已經(jīng)決定了p與cBase類(lèi)相結(jié)合的關(guān)系,這種結(jié)合被稱為靜態(tài)結(jié)合(又被稱為靜態(tài)聯(lián)編)。靜態(tài)結(jié)合方式是在編譯階段就確定好的,這種結(jié)合方式在程序運(yùn)行過(guò)程中是不能改變的。 為了消除由于靜態(tài)結(jié)合所帶來(lái)的不方便性,也就是在程序運(yùn)行過(guò)程中,可以根據(jù)需要利用指針p來(lái)調(diào)用派生類(lèi)中的disp( )函數(shù),就必須要做到在每次將對(duì)象的地址賦給指針p時(shí),都能夠重新確定指針p與相應(yīng)類(lèi)的結(jié)合關(guān)系,也就是采用動(dòng)態(tài)結(jié)合方式。 p=&dt1; /p與cBase結(jié)合p=&

16、dt3; /p與cDerived類(lèi)結(jié)合 能夠?qū)崿F(xiàn)動(dòng)態(tài)結(jié)合方式,也就實(shí)現(xiàn)了多態(tài)性。而要實(shí)現(xiàn)動(dòng)態(tài)結(jié)合方式,就需要采用虛函數(shù)。 前面已經(jīng)介紹過(guò),只有采用動(dòng)態(tài)結(jié)合方式才能實(shí)現(xiàn)多態(tài)性。在C+語(yǔ)言中,虛函數(shù)是向系統(tǒng)表明采用動(dòng)態(tài)結(jié)合方式的函數(shù),它是通過(guò)在一般成員函數(shù)的前面加上virtual關(guān)鍵字來(lái)定義的。定義了虛函數(shù)以后,就可以實(shí)現(xiàn)程序的多態(tài)性。 7.2.2 虛函數(shù)【例7.3】編一程序,用于說(shuō)明虛函數(shù)的定義與使用 #include class cBase_v int va; int vb;public: cBase_v( ) va=0; vb=0; cBase_v(int a, int b) va=a; v

17、b=b; void set_va(int dt) va=dt; void set_vb(int dt) vb=dt; int get_va( ) return va; int get_vb( ) return vb; virtual void disp( ); ;void cBase_v:disp( ) cout “va=” va n; cout “vb=” vb n; class cDerived_v:public cBase_v int vc;public: cDerived_v( ); cDerived_v(int a, int b, int c); void set_vc(int dt

18、) vc=dt; int get_vc( ) return vc; void disp( );cDerived_v:cDerived_v( ):cBase_v( ) vc=0;cDerived_v:cDerived_v(int a,int b,intc) :cBase_v(a,b) vc=c; void cDerived_v:disp( ) cBase_v:disp( ); cout “vc=” vc disp( ); /p與cBase_v對(duì)象結(jié)合,調(diào)用 /cBase_v的disp( )函數(shù) p=&dt3; /設(shè)定cDerived_v對(duì)象的地址 p-disp( ); /p與cDerived_v

19、對(duì)象結(jié)合, /調(diào)用cDerived_v的disp( )函數(shù) 執(zhí)行結(jié)果:va=10vb=20va=300 vb=400vc=500va=10vb=20va=300 vb=400vc=500說(shuō) 明 (1)虛函數(shù)的定義是在基類(lèi)的函數(shù)的前面加上virtual關(guān)鍵字。在一個(gè)類(lèi)中可以定義任意多個(gè)虛函數(shù)。 (2)當(dāng)某個(gè)函數(shù)(比如disp( )函數(shù))被定義為虛函數(shù)以后,就可以利用指向基類(lèi)的指針來(lái)實(shí)現(xiàn)程序的多態(tài)性。也就是說(shuō),假如p被定義為指向基類(lèi)的指針,如果將基類(lèi)對(duì)象的地址賦給p,則利用p來(lái)調(diào)用的disp( )函數(shù)是基類(lèi)中的disp( )函數(shù);如果將派生類(lèi)對(duì)象的地址賦給p,則利用p來(lái)調(diào)用的disp( )函數(shù)是派

20、生類(lèi)中的disp( )函數(shù)。 (3)需要注意的是,為了實(shí)現(xiàn)多態(tài),上述程序中的指針p必須被定義為指向基類(lèi)對(duì)象的指針,而不能被定義為指向派生類(lèi)對(duì)象的指針,同時(shí)需要將派生類(lèi)對(duì)象的地址賦給指針p。在此前提下,我們考慮如下的調(diào)用過(guò)程: (4)由于虛函數(shù)是實(shí)現(xiàn)多態(tài)的前提條件,所以如何判斷派生類(lèi)中的某個(gè)函數(shù)funcX是否為虛函數(shù)就顯得尤為重要。funcX與基類(lèi)的虛函數(shù)A有相同的函數(shù)名。funcX與基類(lèi)的虛函數(shù)A有相同的參數(shù)個(gè)數(shù)及其類(lèi)型。funcX與基類(lèi)的虛函數(shù)A有相同的返回值類(lèi)型。其判斷規(guī)則如下: 如果利用p所調(diào)用的函數(shù)只在基類(lèi)中存在,而派生類(lèi)中沒(méi)有,這時(shí)直接調(diào)用基類(lèi)中的函數(shù)。 如果利用p所調(diào)用的函數(shù)只在派

21、生類(lèi)中存在,而基類(lèi)中沒(méi)有,這時(shí)將發(fā)生編譯錯(cuò)誤。 如果利用p所調(diào)用的函數(shù)在基類(lèi)和派生類(lèi)中都存在,但不是虛函數(shù),這時(shí)將調(diào)用基類(lèi)中的函數(shù)。 如果利用p所調(diào)用的函數(shù)在基類(lèi)和派生類(lèi)中都存在且是虛函數(shù),這時(shí)將調(diào)用派生類(lèi)中的函數(shù)?!纠?.4】編一程序,分別利用指針、引用和對(duì)象名來(lái)調(diào)用虛函數(shù),并對(duì)其運(yùn)行結(jié)果進(jìn)行分析。 #include class basepublic: virtual int fun(void) coutbase:funendl; return 10; ;class divide:public basepublic: int fun(void) coutdivide:funfun(); b2

22、.fun(); b3.fun();運(yùn)行結(jié)果: devide:fun devide:fun base:fun將派生類(lèi)對(duì)象的地址賦給指向基類(lèi)的指針或基類(lèi)對(duì)象的引用并調(diào)用虛函數(shù)都可以實(shí)現(xiàn)多態(tài),但將派生類(lèi)對(duì)象賦給基類(lèi)對(duì)象并以此來(lái)調(diào)用虛函數(shù)是不能實(shí)現(xiàn)多態(tài)的。 7.3 純虛函數(shù)和抽象類(lèi)7.3.1 純虛函數(shù) 在上面的例子中,基類(lèi)中的disp( )函數(shù)是一個(gè)虛函數(shù),而且同普通函數(shù)一樣 ,也具有函數(shù)實(shí)體。 如果cBase_v只是作為基類(lèi)來(lái)使用,而絕對(duì)不會(huì)在程序中生成 cBase_v類(lèi)的對(duì)象,這樣基類(lèi)cBase_v中的disp( )函數(shù)的實(shí)體也就不會(huì)被使用,由于未使用的函數(shù)實(shí)體是一種浪費(fèi),因此,可以把此函數(shù)體省略

23、,無(wú)函數(shù)體的虛函數(shù)被稱為純虛函數(shù)。 在基類(lèi)中,純虛函數(shù)只有函數(shù)說(shuō)明,且在函數(shù)說(shuō)明的后面加上“=0”。需要注意的是,在基類(lèi)中說(shuō)明的純虛函數(shù),必須要在派生類(lèi)中給出其定義。 【例7.5】編一程序,用于說(shuō)明純虛函數(shù)的定義和使用。 #include class cBase_v /基類(lèi) int va; int vb; public: cBase_v( ) va=0; vb=0; cBase_v(int a, int b) va=a; vb=b; void set_va(int dt) va=dt; void set_vb(int dt) vb=dt; int get_va( ) return va; in

24、t get_vb( ) return vb; virtual void disp( )=0;class cDerivedA:public cBase_v int vc; public: cDerivedA( ); cDerivedA(int a, int b, int c); void set_vc(int dt) vc=dt; int get_vc( ) return vc; void disp( );cDerivedA:cDerivedA( ):cBase_v( ) vc=0; cDerivedA:cDerivedA(int a,int b, int c) :cBase_v(a,b) vc

25、=c; void cDerivedA:disp( ) /cBase_v:disp( ); /disp( )函數(shù)沒(méi)定義,不能使用 cout “va=” get_va( ) n; cout “vb=” get_vb( ) n; cout “vc=” vc n;class cDerivedB:public cBase_v int vc; public: cDerivedB( ); cDerivedB(int a, int b, int c); void set_vc(int dt) vc=dt; int get_vc( ) return vc; void disp( );cDerivedB:cDer

26、ivedB( ):cBase_v( ) vc=0;cDerivedB:cDerivedB(int a,int b,int c) :cBase_v(a,b) vc=c; void cDerivedB:disp( ) / cBase_v:disp( ); /基類(lèi)中的disp( )函數(shù)無(wú)定義不能使用 cout “va=” get_va( ) n; cout “vb=” get_vb( ) n; cout “vc=” vc disp( ); /p與cDerivedA對(duì)象結(jié)合,調(diào)用cDerivedA的disp( ) p=(cDerivedA *)&dtb; /將cDerivedB對(duì)象的地址賦給p p-d

27、isp( ); /p與cDerivedB對(duì)象結(jié)合,調(diào)用cDerivedB的disp( ) 執(zhí)行結(jié)果: va=300 vb=400 vc=500 va=600 vb=700 vc=800說(shuō)明 (1)如果在一個(gè)類(lèi)的定義中包含純虛函數(shù),則不能定義該類(lèi)的對(duì)象。由于cBase_v類(lèi)中包含虛函數(shù)定義,因此,上面的語(yǔ)句將出錯(cuò)。cBase_v dt1(10,20);例如: (2)需要注意的是,派生類(lèi)對(duì)象的地址可以賦給基類(lèi)對(duì)象的指針,但是,基類(lèi)對(duì)象的地址是不能賦給派生類(lèi)對(duì)象指針的。同時(shí),具有相同基類(lèi)的兩個(gè)派生類(lèi)對(duì)象的指針也不能自動(dòng)地互相轉(zhuǎn)換,而需要進(jìn)行強(qiáng)制類(lèi)型轉(zhuǎn)換。 p=(cDerivedA *)&dtb;例如

28、:7.3.2 抽象類(lèi) 包含純虛函數(shù)的類(lèi)被稱為抽象類(lèi),也就是在抽象類(lèi)中包含沒(méi)有函數(shù)體定義的函數(shù)。以抽象類(lèi)作為基類(lèi)來(lái)生成派生類(lèi)時(shí),必須要在派生類(lèi)中給出純虛函數(shù)的定義,如果在派生類(lèi)中也沒(méi)有給出純虛函數(shù)的定義,則該派生類(lèi)也自動(dòng)成為抽象類(lèi)。需要注意的是,抽象類(lèi)是不能定義其對(duì)象的。7.4虛析構(gòu)函數(shù) 在C+語(yǔ)言中,不能將構(gòu)造函數(shù)定義為虛函數(shù),但可以將析構(gòu)函數(shù)定義為虛函數(shù)。定義虛析構(gòu)函數(shù)的目的是為了在調(diào)用析構(gòu)函數(shù)時(shí)實(shí)現(xiàn)多態(tài),以便進(jìn)行所希望的內(nèi)存釋放等后處理工作。virtual 類(lèi)名( );虛析構(gòu)函數(shù)的原型如下: 【例7.6】編一實(shí)現(xiàn)繼承功能的程序,在基類(lèi)和派生類(lèi)中都給出析構(gòu)函數(shù)的定義,通過(guò)刪除指向基類(lèi)的指針來(lái)

29、觀察析構(gòu)函數(shù)的執(zhí)行過(guò)程。 #include class cBase int x; public: cBase(int a=0) x=a; cBase( ) cout Enter cBase destructor.n; virtual void disp( ) cout x= x n; ; class cDerived: public cBase int y; public: cDerived(int a=0): cBase( ) y=a; cDerived( ) cout Enter cDerived destructor.n; void disp( ) cBase:disp( ); cout

30、 y= y disp( ); delete p;執(zhí)行結(jié)果: x=0 y=10Enter cBase destructor.說(shuō)明: 由運(yùn)行結(jié)果可以看出,當(dāng)希望通過(guò)指向基類(lèi)的指針來(lái)刪除派生類(lèi)對(duì)象時(shí),調(diào)用的卻是基類(lèi)的析構(gòu)函數(shù),而沒(méi)有調(diào)用派生類(lèi)的析構(gòu)函數(shù),這樣,就無(wú)法利用派生類(lèi)的析構(gòu)函數(shù)來(lái)進(jìn)行一些后處理工作(如內(nèi)存釋放等)。 為了解決這一問(wèn)題,可以將基類(lèi)的析構(gòu)函數(shù)說(shuō)明為虛函數(shù)(這樣,由基類(lèi)派生而來(lái)的所有類(lèi)中的析構(gòu)函數(shù)也都成為虛函數(shù)),這樣,在通過(guò)基類(lèi)的指針來(lái)刪除派生類(lèi)對(duì)象時(shí)就能夠調(diào)用派生類(lèi)的析構(gòu)函數(shù)。 例如,對(duì)基類(lèi)的析構(gòu)函數(shù)進(jìn)行如下修改:virtual cBase( ) cout disp( );ma

31、in函數(shù)改為如下:則在main函數(shù)執(zhí)行結(jié)束時(shí)并不能自動(dòng)調(diào)用析構(gòu)函數(shù),這是由于盡管p是在main函數(shù)內(nèi)定義的局部變量,但p是指針而不是對(duì)象,因此不能夠自動(dòng)釋放由p所指向的對(duì)象。 (4)由程序的運(yùn)行結(jié)果可知,如果調(diào)用了派生類(lèi)的析構(gòu)函數(shù),則將自動(dòng)調(diào)用其基類(lèi)的析構(gòu)函數(shù)。7.5 繼承的種類(lèi) 7.5.1 多重繼承 在C+語(yǔ)言中,只繼承一個(gè)基類(lèi)而生成的派生類(lèi)被稱為單一繼承。通過(guò)繼承多個(gè)基類(lèi)而生成的派生類(lèi)被稱為多重繼承。多重繼承的定義/基類(lèi) cMultiA的定義class cMultiA ;/基類(lèi) cMultiB的定義 ;/經(jīng)多重繼承而產(chǎn)生的派生類(lèi)cMultiAB的定義class cMultiAB: publ

32、ic cMultiA, public cMultiB ; 經(jīng)過(guò)該定義以后, cMultiAB也就繼承了cMultiA和cMultiB中的非private成員。在定義多重繼承時(shí),每個(gè)基類(lèi)之間用逗號(hào)隔開(kāi)。另外,在每個(gè)基類(lèi)名的前面都要加上public、protected或private說(shuō)明,如果省略不寫(xiě),則其缺省為private。 下面的例子說(shuō)明了多重繼承的定義和使用其中:cMint類(lèi)用于管理int型數(shù)據(jù)。cMdbl類(lèi)用于管理double型數(shù)據(jù)cMids類(lèi)在繼承了cMint和cMdbl類(lèi)的同時(shí), 追加了對(duì)字符串進(jìn)行管理的功能?!纠?.7】編一程序,利用多重繼承來(lái)實(shí)現(xiàn)對(duì)整數(shù)、浮點(diǎn)數(shù)以及字符串進(jìn)行管理的

33、功能。#include #include class cMint /基類(lèi) int idt;public: cMint( ) idt=0; cMint(int dt) idt=dt; void set_dt(int dt) idt=dt; int get_dt( ) return idt; void disp( ) cout “idt=” idt n;class cMdbl /基類(lèi) double ddt;public: cMdbl( ) ddt=0; cMdbl(double dt) ddt=dt; void set_dt(double dt) ddt=dt; double get_dt( )

34、return ddt; void disp( ) cout “ddt=” ddt n;class cMids: public cMint, public cMdbl char ss256;public: cMids( ): cMint( ), cMdbl( ) strcpy(ss,“ ”); cMids(int d1,double d2,char *str); void set_dt(int d1,double d2,char *str); char *get_dt( ) return ss; void disp( );cMids:cMids(int d1,double d2,char *st

35、r) : cMint(d1), cMdbl(d2) strcpy(ss,str);void cMids:set_dt(int d1,double d2,char *str) cMint:set_dt(d1); cMdbl:set_dt(d2); strcpy(ss,str); void cMids:disp( ) cMint:disp( ); cMdbl:disp( ); cout “ss=” ss n; void main( ) cMids dt(100,2.22,“AAAAA”); dt.disp( ); dt.set_dt(300,4.44,“BBBBB”); dt.disp( ); d

36、t.cMint:set_dt(500); dt.cMdbl:set_dt(6.66); cout “int:” dt.cMint:get_dt( ) n; cout “dbl:” dt.cMdbl:get_dt( ) n; cout “str:” dt.get_dt( ) n; 執(zhí)行結(jié)果:idt=100ddt=2.22ss=AAAAAidt=300ddt=4.44ss=BBBBBint:500dbl:6.66str:BBBBBclass cMids: public cMint, public cMdbl ;從上例中得出多重繼承的定義與特點(diǎn):(1)多重繼承的定義 它表示通過(guò)對(duì)cMint和cMdb

37、l類(lèi)進(jìn)行public繼承之后,生成了派生類(lèi)cMids。每個(gè)基類(lèi)之間通過(guò)逗號(hào)隔開(kāi),每個(gè)基類(lèi)名的前面都需要加上public、protected或private說(shuō)明,如果不寫(xiě)的話,其缺省值為private。(2)在派生類(lèi)中調(diào)用多個(gè)基類(lèi)的構(gòu)造函數(shù)表示從cMids類(lèi)的構(gòu)造函數(shù)中調(diào)用cMint和cMdbl兩個(gè)基類(lèi)的構(gòu)造函數(shù),以便完成對(duì)基類(lèi)中數(shù)據(jù)成員的初始化工作。cMids:cMids(int d1,double d2,char *str):cMint(d1), cMdbl(d2) strcpy(ss,str); (3)在派生類(lèi)的成員函數(shù)中,若需要可使用作用域限定運(yùn)算符來(lái)調(diào)用基類(lèi)中的成員函數(shù)。 若在派生類(lèi)中

38、需要調(diào)用基類(lèi)中的同名的成員函數(shù),則需要使用作用域限定運(yùn)算符。 void cMids:set_dt(int d1,double d2,char *str) cMint:set_dt(d1); cMdbl:set_dt(d2); strcpy(ss,str); 例 如: 在cMids類(lèi)的set_dt( )成員函數(shù)中,通過(guò)使用作用域限定運(yùn)算符:來(lái)調(diào)用基類(lèi)中的同名set_dt( )函數(shù)。這里,由于派生類(lèi)中的函數(shù)和基類(lèi)中的函數(shù)重名,所以,如果不使用作用域限定運(yùn)算符的話,將調(diào)用派生類(lèi)中同名函數(shù)。 (4)在一般函數(shù)中若需要的話,也可以使用作用域限定運(yùn)算符 當(dāng)基類(lèi)中的成員函數(shù)和派生類(lèi)中的成員函數(shù)同名時(shí),若要在

39、一般函數(shù)中(不是類(lèi)中的成員函數(shù))利用派生類(lèi)對(duì)象來(lái)調(diào)用基類(lèi)中的同名函數(shù)時(shí),需要使用作用域限定運(yùn)算符,若不使用作用域限定運(yùn)算符,則調(diào)用的將是派生類(lèi)中的同名函數(shù)。 cMids dt(100,2.22,“AAAAA”); dt.cMint:set_dt(500); /cMint類(lèi)中的set_dt( )函數(shù) dt.cMdbl:set_dt(6.66); /cMdbl類(lèi)中的set_dt( )函數(shù) dt.set_dt(300,4.44,“BBBBB”); /cMids類(lèi)中的set_dt( )函數(shù) cout “int:” dt.cMint:get_dt( ) n; cout “dbl:” dt.cMdbl:g

40、et_dt( ) n; cout “str:” dt.get_dt( ) n;例如 有時(shí)為了增加程序的可讀性,即使在不需要作用域限定符的地方,我們也可以加上作用域限定運(yùn)算符。dt.cMids:set_dt(300,4.44,“BBBBB”);例如: (5)在一般函數(shù)中利用派生類(lèi)對(duì)象來(lái)調(diào)用基類(lèi)中的成員函數(shù)時(shí),需要注意對(duì)基類(lèi)的訪問(wèn)控制設(shè)置。 前面已經(jīng)提到,對(duì)基類(lèi)的訪問(wèn)控制一般設(shè)置為public,但這不是絕對(duì)。如果對(duì)基類(lèi)的訪問(wèn)控制被設(shè)置為private或protected,則在一般函數(shù)中調(diào)用基類(lèi)中的函數(shù)時(shí),需要注意其訪問(wèn)權(quán)限。例 如class cMids: private cMint, privat

41、e cMdbl ;class cMids: protected cMint,protected cMdbl ;或:或:class cMids: cMint, cMdbl /缺省為private ; 在上述定義的基礎(chǔ)上,如果在main( )函數(shù)中出現(xiàn)下述語(yǔ)句則將出錯(cuò): dt.cMint:set_dt(500);dt.cMdbl:set_dt(6.66); 由于對(duì)基類(lèi)的訪問(wèn)控制是protected或private,所以基類(lèi)中的set_dt( )成員函數(shù)在派生類(lèi)中的訪問(wèn)權(quán)限就成為protected或private,這樣,在main( )函數(shù)中就不能直接調(diào)用這些函數(shù)。 7.5.2 直接繼承和間接繼承

42、派生類(lèi)是通過(guò)繼承某個(gè)(或某些)基類(lèi)而產(chǎn)生的,同時(shí)派生類(lèi)本身也可以作為另外一個(gè)派生類(lèi)的基類(lèi),這樣就形成了一種多層次的繼承關(guān)系。 例如 class cLayA /cLayA是cLayB的基類(lèi) ; class cLayB: public cLayA /cLayB是cLayC的基類(lèi) ; class cLayC: public cLayB ; 在上述定義的基礎(chǔ)上,我們說(shuō)cLayB直接繼承了cLayA類(lèi),cLayC直接繼承了cLayB類(lèi),而cLayC間接繼承了cLayA類(lèi),這就是直接繼承和間接繼承的關(guān)系。 7.6 多重基類(lèi)和虛擬基類(lèi) 一個(gè)派生類(lèi)可以具有多個(gè)基類(lèi),這就是已經(jīng)介紹過(guò)的多重繼承,也可以稱為多重基

43、類(lèi)。類(lèi)B類(lèi)C類(lèi)D圖中,類(lèi)D繼承了類(lèi)B和類(lèi)C。 在利用多重繼承來(lái)生成派生類(lèi)時(shí),如果不特別注意的話往往會(huì)出現(xiàn)一些問(wèn)題。例如,下圖給出了一種多重繼承關(guān)系。類(lèi)A類(lèi)B類(lèi)C類(lèi)D上圖給出了一種多重繼承關(guān)系,這種多重繼承關(guān)系的C+語(yǔ)言描述如下: class cBase_A public: int getx( ) ; class cBase_B: public cBase_A ; class cBase_C: public cBase_A ; class cDerived_D: public cBase_B, public cBase_C ; 注:從語(yǔ)法角度左面的定義沒(méi)有錯(cuò)誤,如下述用法正確:cDerived_

44、D x;int A, B;A=x.cBase_B:getx( );B=x.cBase_C:getx( );但下述用法錯(cuò)誤:A=x.getx( );/無(wú)法確定要調(diào)用 的是哪個(gè)函數(shù)。/下述用法也是錯(cuò)的A=x.cBase_B:cBase_A:getx( ); 由上述繼承過(guò)程不難看出,在類(lèi)cDerived_D中包含兩個(gè)cBase_A類(lèi)的對(duì)象,這樣在使用時(shí)將會(huì)出現(xiàn)問(wèn)題。為了解決這一問(wèn)題,在C+語(yǔ)言中提出了虛擬基類(lèi)的概念,利用虛擬基類(lèi)就可以解決上述問(wèn)題。虛擬基類(lèi)是在定義派生類(lèi)時(shí),在基類(lèi)名的前面通過(guò)使用virtual關(guān)鍵字來(lái)實(shí)現(xiàn)的。 例如 class cBase_A ; class cBase_B: vir

45、tual public cBase_A ; class cBase_C: virtual public cBase_A ; class cDerived_D: public cBase_B, public cBase_C ; 在基類(lèi)名的前面加上virtual關(guān)鍵字,表示該基類(lèi)為虛擬基類(lèi)。以這種方式所生成的派生類(lèi)cDrived_D類(lèi)中,只包含一個(gè)cBase_A對(duì)象。虛擬基類(lèi)除了能保證只有一個(gè)基類(lèi)對(duì)象被繼承以外,同一般的基類(lèi)是一樣的。 例(1): 考慮非虛擬基類(lèi)的情況#include iostream.hclass cBase_A int x;public:cBase_A( )x=10;int g

46、etx()return x;void setx(int a)x=a; ; class cBase_B: public cBase_A ; class cBase_C: public cBase_A ; class cDerived_D: public cBase_B, public cBase_C ;void main( ) cDerived_D x; int y,z,s; y=x.cBase_B:getx( ); x.cBase_C:setx(11); y=x.cBase_B:getx( ); z=x.cBase_C:getx( ); s=x.cBase_B:getx( ); cout y=

47、yn; cout z=zn; cout s=sn;執(zhí)行結(jié)果:y=10,z=11,s=10當(dāng)沒(méi)有指定為虛擬基類(lèi)時(shí),每個(gè)基類(lèi)的數(shù)據(jù)成員互不影響,即: x.cBase_C:setx(11);語(yǔ)句所設(shè)置的數(shù)據(jù),對(duì)cBase_B類(lèi)的x成員沒(méi)有任何影響.此時(shí),不能使用語(yǔ)句: x.setx(1);或y=x.getx( );例(2): 考慮虛擬基類(lèi)的情況僅對(duì)例(1)的class cBase_B和class cBase_C進(jìn)行如下修改,其他程序不變: class cBase_B: virtual public cBase_A ; class cBase_C: virtual public cBase_A ;執(zhí)行結(jié)果如下:y=11,z=11,s=11在使用虛基類(lèi)的情況下,只要有一個(gè)基類(lèi)的成員被修改了,就全修改了,因只有一個(gè)基類(lèi). 此時(shí)可以使用如下語(yǔ)句: x.setx(1);或y=x.getx( );而無(wú)需指定基類(lèi)名.7.7 繼承方式下的構(gòu)造與析構(gòu) 在采用繼承方式所設(shè)計(jì)的程序中,通過(guò)繼承而生成的派生類(lèi)可能擁有多個(gè)基類(lèi),同時(shí),在派生類(lèi)中和基類(lèi)中還可能包含多個(gè)對(duì)象數(shù)據(jù)成員,在這種情況下,每個(gè)對(duì)象中的構(gòu)造函數(shù)和析構(gòu)函數(shù)是按照怎樣的順序執(zhí)行的呢?這就是本節(jié)要學(xué)習(xí)的內(nèi)容。 【例7.8】編一程序,利用多重繼承來(lái)生成派生類(lèi),同時(shí)在派生類(lèi)中和基類(lèi)中定義多個(gè)對(duì)象數(shù)

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 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ì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論