程序設(shè)計-第5章繼承與派生_第1頁
程序設(shè)計-第5章繼承與派生_第2頁
程序設(shè)計-第5章繼承與派生_第3頁
程序設(shè)計-第5章繼承與派生_第4頁
程序設(shè)計-第5章繼承與派生_第5頁
已閱讀5頁,還剩77頁未讀 繼續(xù)免費閱讀

下載本文檔

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

文檔簡介

1第二部分面向?qū)ο蟮某绦蛟O(shè)計

第3章類和對象(一)第4章類和對象(二)第5章繼承和派生第6章虛函數(shù)與多態(tài)性第7章運算符重載第8章模板第9章標準模板庫STL第10章C++語言的輸入和輸出2第5章繼承和派生本章重點:繼承與派生的概念;派生類的構(gòu)造函數(shù)和析構(gòu)函數(shù)的執(zhí)行順序與規(guī)則;多繼承的聲明與實現(xiàn);基類成員訪問原則;賦值兼容性;虛基類的概念;35.1繼承與派生的概念

繼承是面向?qū)ο蟪绦蛟O(shè)計中重要的特性。繼承主要是指在己有類的或稱為基類的基礎(chǔ)上創(chuàng)建新類的過程,這個新類就是派生類。派生類自動的包含了基類的成員,包括所有的數(shù)據(jù)和操作,而且它還可以增加自身新的成員4在C++中,一個派生類可以從一個基類派生,也可以從多個基類派生,從一個基類派生的稱為單繼承,如圖5.1。圖5.2中的樹型結(jié)構(gòu)圖可以體現(xiàn)學生體系的概念。圖5.1單繼承

圖5.2類的層次樹型結(jié)構(gòu)圖5一個派生類從兩個或多個基類派生則稱為多繼承,如圖5.3所示,它使一個類可以融合了多個類的特征,例如在現(xiàn)實生活中的在職研究生的概念,他是在職人員,又是研究生,如圖5.4所示,從圖中還可以看到一個有趣的現(xiàn)象,在職人員類本身是單繼承的基類,教師和職員都是它的具體子類,而其又是在職研究生的多重基類,提供在職人員的基本特征。圖5.3多繼承

圖5.4單繼承與多繼承

6繼承機制除了支持軟件復用外,還具備以下三個作用:對事物進行分類。支持軟件的增量開發(fā)。對概念進行組合。

7前面的知識中,我們學習了如何定義類和如何實現(xiàn)類的抽象與封裝,通常在不同的類中,數(shù)據(jù)成員和函數(shù)成員都是不同的,但對于某些特定的問題,有時候兩個類的基本或大部分內(nèi)容是相同的,在圖5.2中給出了學生體系的概念,我們利用現(xiàn)有知識可以首先聲明了一類來描述學生這一基本概念,如下:classStudent{private: intnumber; charname[20];public:Student(){ number=0;name[0]=’\0’;}VoidSetValue(intn,char*s1){ number=n; Strcpy(name,s1);} voidPrint() { cout<<”Number:”<<number<<endl; cout<<”Name:”<<name<<endl; }};8如果現(xiàn)在需要一個新類UGStudent來描述大學生概念,除上述的基本成員外,還需要用到年齡、年級等信息,我們可以如下定義此類:classUGStudent{private: intnumber; charname[20]; intage; intgrade;public: voidPrint() { cout<<”Number:”<<number<<endl; cout<<”Name:”<<name<<endl;cout<<”Age:”<<age<<endl;cout<<”Grade:”<<grade<<endl;

}};95.2派生類的聲明

在C++中類的繼承關(guān)系可以用如下語法表示:class派生類名:繼承方式

基類名{

派生類成員聲明};需要注意的是,基類的構(gòu)造函數(shù)和析構(gòu)函數(shù)不能被派生類繼承,派生類若要初始化基類的數(shù)據(jù)成員必須在構(gòu)造函數(shù)中初始化。10【例5.1】用繼承重新定義UGStudent類。

/*05_01.cpp*/classUGStudent:publicStudent{private: intage; intgrade;public: UGStudent(){ SetValue(0,””); age=0;grade=0;}

UGStudent(intn,char*s1,inta,intg){ SetValue(n,s1); age=a;grade=g;} voidPrintExtra() { cout<<”Age:”<<age<<endl;cout<<”Grade:”<<grade<<endl;

}};11用主函數(shù)進行測試:intmain(){ UGStudentst1(100,”wang”,18,1); st1.Print(); //調(diào)用基類的函數(shù)

st1.PrintExtra(); //調(diào)用派生類新定義的函數(shù)

return0;} 12Number:100Name:wangAge:18Grade:1程序的運行結(jié)果為:13圖5.5基類與派生類中的成員

145.3派生類的訪問屬性

類的成員可以分為public(公有)、protected(保護)和private(私有)三種訪問權(quán)限。類的非靜態(tài)成員函數(shù)可以訪問類中的所有成員,但是通過類的“對象.成員”方式(在類的作用域之外),則只能訪問該類的公有成員。類的繼承方式有公有繼承(public)、保護繼承(protected)和私有繼承(private)三種。不同的繼承方式,導致原有基類成員在派生類中的訪問屬性也有所不同。

15表5.1不同繼承方式下的訪問控制權(quán)限基類成員的權(quán)限繼承方式publicprotectedprivatepublic在派生類中為public在派生類中為protected在派生類中為private派生類的成員函數(shù)和類的作用域之外,都可以直接訪問派生類的成員函數(shù)可以直接訪問派生類的成員函數(shù)可以直接訪問protected在派生類中為protected在派生類中為protected在派生類中為private派生類的成員函數(shù)可以直接訪問派生類的成員函數(shù)可以直接訪問派生類的成員函數(shù)可以直接訪問private在派生類中被隱藏,無法訪問在派生類中被隱藏,無法訪問在派生類中被隱藏,無法訪問任何方式都不能直接訪問,但可以通過基類的public、protected成員函數(shù)間接訪問任何方式都不能直接訪問,但可以通過基類的public、protected成員函數(shù)間接訪問訪問任何方式都不能直接訪問,但可以通過基類的public、protected成員函數(shù)間接訪問訪問16對于靜態(tài)成員來說,與普通成員函數(shù)組合,將產(chǎn)生以下兩種情況:(1)派生類中靜態(tài)函數(shù)對基類中靜態(tài)成員的訪問。(2)派生類的普通成員函數(shù)要訪問基類中的靜態(tài)成員。靜態(tài)成員的訪問控制變化完全遵循表5.1的規(guī)則,這兩種情況和派生類中普通成員函數(shù)訪問基類中普通成員沒有區(qū)別。

17【例5.2】公有繼承時的訪問控制權(quán)限。

/*05_02.cpp*/#include<iostream>usingnamespacestd;classBase{private: inta;voidfun1(){cout<<a<<endl;}protected:intb;voidfun2(){cout<<c<<endl;}public:intc;voidfun3(){cout<<b<<endl;}voidseta(inti) //修改私有成員a的值{a=i;}int geta() //返回私有成員a的值{returna;}Base(inti,intj,intk) //基類的構(gòu)造函數(shù){a=i;b=j;c=k;}};18classSub:publicBase{private: intd;public:Sub(inti,intj,intk,intm):Base(i,j,k)//派生類構(gòu)造函數(shù){d=m;}voidtest(){//cout<<a<<endl; //錯誤,無法訪問基類的私有成員cout<<b<<endl; //正確,可以訪問基類的保護成員cout<<c<<endl; //正確,可以訪問基類的公有成員//fun1(); //錯誤,無法訪問基類的私有成員fun2(); //正確,可以訪問基類的保護成員fun3(); //正確,可以訪問基類的公有成員seta(10); //正確,間接訪問基類成員acout<<d<<endl; //正確,可以訪問派生類的私有成員}};19intmain(){Baseb1(5,6,7);//cout<<b1.a; //錯誤,無法訪問對象的私有成員//cout<<b1.b; //錯誤,無法訪問對象的保護成員cout<<b1.c<<endl; //正確,可以訪問對象的公有成員cout<<b1.geta()<<endl; //正確,間接訪問對象的私有成員aSubs1(11,15,19,22);s1.test(); //正確,可以訪問對象的公有成員s1.c=200; //正確,可以訪問對象的公有成員s1.fun3(); //正確,可以訪問對象的公有成員return0;}205.4派生類的構(gòu)造函數(shù)和析構(gòu)函數(shù)

在繼承機制中,基類的構(gòu)造函數(shù)和析構(gòu)函數(shù)是不能繼承的。派生類的構(gòu)造函數(shù)負責對來自基類數(shù)據(jù)成員和新增加的數(shù)據(jù)成員進行初始化。所以在執(zhí)行派生類的構(gòu)造函數(shù)時,需要調(diào)用基類的構(gòu)造函數(shù)。215.4.1派生類構(gòu)造函數(shù)和析構(gòu)函數(shù)的執(zhí)行順序

通過繼承,派生類得到了基類的成員,因此派生類對象中既包括自身類的數(shù)據(jù)成員還包括通過繼承從基類中得到的數(shù)據(jù)成員。在派生類中還可用其他類來定義對象作為成員,又涉及到派生類中對象成員的構(gòu)造問題,則當用派生類定義對象后,派生類對象、對象成員、基類對象的構(gòu)造函數(shù)的調(diào)用順序如下:基類的構(gòu)造函數(shù)。對象成員的構(gòu)造函數(shù)(如果有的話),有多個時按聲明的順序。派生類的構(gòu)造函數(shù)。22【例5.3】派生類構(gòu)造實例。

/*05_03.cpp*/#include<iostream>usingnamespacestd;classB{public: B(){cout<<”ConstructB”<<endl

;}};classC{public:

C(){cout<<”ConstructC”<<endl

;}};classD:publicB{private: Cc1;public: D(){cout<<”ConstructD”<<endl

;}};intmain(){ Dd1;return0;}23constructBConstructCConstructD程序的運行結(jié)果為:24析構(gòu)函數(shù)與構(gòu)造函數(shù)執(zhí)行的順相反,將按如下順序執(zhí)行:(1)派生類的構(gòu)造函數(shù);(2)對象成員的構(gòu)造函數(shù)(如果有的話),有多個時與聲明的順序相反;(3)基類對象的析構(gòu)函數(shù)。25對例5.3中的程序進行改造,為每一個類添加析構(gòu)函數(shù),然后再進行測試。

classB { public: B(){cout<<”ConstructB”<<endl;} ~B(){cout<<”DeconstructB”<<endl;} }; classC { public: C(){cout<<”ConstructC”<<endl;} ~C(){cout<<”DeconstructC”<<endl;} }; classD:publicB { private: Cc1; public: D(){cout<<”ConstructD”<<endl;} ~D(){cout<<”DeconstructD”<<endl;} }; intmain() { Dd1; return0; }26ConstructBConstructCConstructDDestructDDestructCDestructB程序的運行結(jié)果為:275.4.2派生類構(gòu)造函數(shù)和析構(gòu)函數(shù)的構(gòu)造規(guī)則

在C++中,類的機制非常清楚、嚴格地劃分了各自的權(quán)限和責任。是哪個類的操作,必須由那個類調(diào)用;是誰的對象,就必須由該類的構(gòu)造函數(shù)來完成對其構(gòu)造的工作。因此,對派生類中基類成員的構(gòu)造,必須由基類構(gòu)造函數(shù)完成,而不能由派生類的構(gòu)造函數(shù)越權(quán)去構(gòu)造。派生類構(gòu)造函數(shù)主要負責調(diào)用基類構(gòu)造函數(shù)并提供基類構(gòu)造函數(shù)所需的參數(shù)。28下面分兩種情況討論派生類對象的構(gòu)造:

1.如基類中定義了默認構(gòu)造函數(shù),且該默認構(gòu)造函數(shù)能夠完成派生類對象中基類成員的構(gòu)造,則派生類構(gòu)造函數(shù)無需顯式調(diào)用基類構(gòu)造函數(shù),直接調(diào)用基類的默認構(gòu)造函數(shù)即可,這是一種較為簡單的情況,例5.3中的繼承就屬于此類情況,下面的例子也可以說明這一點。29【例5.4】分析下面程序的輸出結(jié)果。

/*05_04.cpp*/#include<iostream> usingnamespacestd; classBase { public: Base(){a=0;} Base(inti){a=i;} protected: inta; }; classDerived:publicBase { public: Derived(){b=0;} Derived(inti){b=i;} voidPrint() { cout<<"a="<<a<<",b="<<b<<endl; } private: intb; };

30intmain() { Derivedd1; Derivedd2(12); d1.Print(); d2.Print(); return0;}31a=0,b=0a=0,b=12程序的運行結(jié)果為:322.若基類中定義了有參數(shù)的構(gòu)造函數(shù),或者所定義的默認構(gòu)造函數(shù)不能完成基類成員的構(gòu)造,則必須通過派生類構(gòu)造函數(shù)顯式向調(diào)用基類的構(gòu)造函數(shù),向帶參數(shù)的構(gòu)造函數(shù)傳遞參數(shù),這需要用到“成員初始化列表”的語法。另外,對于派生類中普通數(shù)據(jù)成員的初始化,以及對象成員的構(gòu)造也可以放在成員初始化列表中完成。此時這些以逗號隔開的基類構(gòu)造函數(shù)調(diào)用數(shù)據(jù)成員初始化和構(gòu)造的放置順序可以是任意的。因此派生類的構(gòu)造函數(shù)定義一般格式如下:派生類名(參數(shù)列表):基類構(gòu)造函數(shù)(參數(shù)列表1),子對象成員1(參數(shù)列表)……{

派生類構(gòu)造函數(shù)體}33【例5.5】派生類構(gòu)造函數(shù)舉例。

/*05_05.cpp*/#include<iostream> #include<cstring> usingnamespacestd; classDate //日期類 {

private: intyear,mon,day; //年、月、日成員變量

public: Date(inty=2009,intm=6,intd=10)//構(gòu)造函數(shù) {

year=y;mon=m;day=d; } voidPrint() //輸出數(shù)據(jù),格式為:年-月-日 {

cout<<year<<"-"<<mon<<"-"<<day<<endl; } };34classStudent //定義學生基類 {

protected: intnumber; //數(shù)據(jù)成員

charname[20]; charsex; public: Student() //重定義的默認構(gòu)造函數(shù) {

number=0; strcpy(name,"Noname"); //默認名字

sex='M'; //默認性別,男性(male)d } Student(intn,char*s,charx)//帶參數(shù)的構(gòu)造函數(shù) {

number=n; strcpy(name,s); sex=x; } };35//大學生派生類

classUGStudent:publicStudent { public: UGStudent(intn,char*s,charx,inta,inty,intm,intd): Student(n,s,x),birth(y,m,d) { age=a; } UGStudent() //此處省略了Student()調(diào)用 {

age=0; } voidPrint()//輸出信息 {

cout<<"number:"<<number<<endl; cout<<"name:"<<name<<endl; cout<<"sex:"<<sex<<endl; cout<<"age:"<<age<<endl; cout<<"birthday:"; birth.Print(); } private: intage; Datebirth; //對象成員 };36//主函數(shù)

intmain() { UGStudentst1; //用派生的默認構(gòu)造函數(shù)定義對象

UGStudentst2(1001,"Zhang",'F',20,2009,6,11);//帶參數(shù)構(gòu)造

st1.Print(); st2.Print(); return0; }37number:0name:Nonamesex:Mage:0birthday:2009-6-10number:1001name:Zhangsex:Fage:20birthday:2009-6-11程序的運行結(jié)果為:385.5多繼承

5.5.1多繼承的聲明

多繼承下派生類的聲明格式如下:class派生類名:繼承方式1基類名,繼承方式2基類名2……

{派生類類體;};其中,繼承方式1、繼承方式2…是三種繼承方式:public、private和protected之一。

39如下為最基本的定義形式:

classB1 {

… }; classB2 {

… }; classD:publicB1,publicB2 {

…};40圖5.6多繼承類D中的成員

415.5.2多繼承的構(gòu)造函數(shù)與析構(gòu)函數(shù)

在多繼承的情況下,派生類的構(gòu)造函數(shù)格式如下:派生類名(參數(shù)列表):基類名1(參數(shù)表1),基類名2(參數(shù)表2)……,子對象名(參數(shù)表n)……{派生類構(gòu)造函數(shù)體;}42多繼承下派生類的構(gòu)造函數(shù)與單繼承下派生類構(gòu)造函數(shù)相似,它必須同時負責該派生類所有基類構(gòu)造函數(shù)的調(diào)用同時,派生類的參數(shù)個數(shù)必須包含完成所有基類初始化所需的參數(shù)個數(shù)。派生類構(gòu)造函數(shù)執(zhí)行順序是先執(zhí)行所有基類的構(gòu)造函數(shù),再執(zhí)行派生類本身構(gòu)造函數(shù)。處于同一層次的各基類構(gòu)造函數(shù)的執(zhí)行順序取決于聲明派生類時所指定的各基類順序,與派生類構(gòu)造函數(shù)中所定義的成員初始化列表的各項順序無關(guān)。另外,析構(gòu)函數(shù)的調(diào)用順序則與構(gòu)造函數(shù)完全相反。43【例5.6】多繼承舉例。

/*05_06.cpp*/#include<iostream> #include<cstring> usingnamespacestd;//定義研究生基類

classGStudent { protected: intnumber; //學號

charname[20]; //名字

charsex; //性別,男性:M,女性:F public: GStudent(intn,char*s,charx)//帶參數(shù)的構(gòu)造函數(shù) {

number=n; strcpy(name,s); sex=x; cout<<"ConstructGStudent."<<endl; } ~GStudent() //析構(gòu)函數(shù) {cout<<"DestructGStudent."<<endl;} };44classEmployee //職員類 {

protected: charename[20]; //職員名字

charjobname[20]; //工作名

public: Employee(char*sn,char*sj) //構(gòu)造函數(shù) {

strcpy(ename,sn); strcpy(jobname,sj); cout<<"ConstructEmployee."<<endl; } ~Employee() //析構(gòu)函數(shù) {cout<<"DestructEmployee."<<endl;} };45//在職研究生類,從兩個基類派生

classGStudentHasJob:publicGStudent,publicEmployee { public: GStudentHasJob(intn,char*s,charx,char*sj): GStudent(n,s,x),Employee(s,sj) {cout<<"ConstructGStudentHasJob."<<endl; } ~GStudentHasJob() {cout<<"DestructGStudentHasJob."<<endl;} voidPrint()//輸出信息 {

cout<<"number:"<<number<<endl; cout<<"name:"<<name<<endl; cout<<"sex:"<<sex<<endl; cout<<"job:"<<jobname<<endl; } };46//主函數(shù)

intmain() { //定義一個在職研究生對象,并對其初始化 GStudentHasJobst(1001,"zhang",'F',"teacher"); st.Print(); return0; }47Construct GStudent.ConstructEmployee.ConstructGStudentHasJob.number:1001name:zhangsex:Fjob:teacherDestructGStudentHasJob.DestructEmployee.DestructGStudent.程序的運行結(jié)果為:485.6基類成員訪問和賦值兼容性

5.6.1基類成員名的限定訪問和名字覆蓋

若多個基類中定義有同名成員,則派生類對這些同名成員的訪問可能存在沖突。為避免可能出現(xiàn)的成員訪間沖突,需要用成員名限定的方法顯式地指定要訪問的成員。49【例5.7】成員訪問沖突。

/*05_07.cpp*/#include<iostream> usingnamespacestd; classMP3Player //mp3播放器類 {

public: voidPlay() //播放音樂操作 {cout<<"Playmp3music."<<endl;} }; classVideoPlayer //視頻播放器類 {

public: voidPlay() //播放視頻操作 {cout<<"Playvideo."<<endl;} }; //新型的mp4播放器類

classMP4Player:publicMP3Player,publicVideoPlayer { public: /*…………*/ };50intmain() { MP4Playermp4; //mp4.Play(); //去掉注釋,本行將產(chǎn)生Play()函數(shù)訪問不明確的錯誤 mp4.MP3Player::Play(); mp4.VideoPlayer::Play(); return0; }51Playmp3music.Playvideo.程序的運行結(jié)果為:52對于多繼承,如果在不同的基類中定義了同名的成員,在派生類中要區(qū)分成員的來源,就必須使用成員名限定方法,即在成員名前加上各自基類的訪問域限制即可。

53【例5.8】多繼承中成員限定法。

/*05_08.cpp*/#include<iostream> usingnamespacestd; classB1 //基類B1 { public: intm; //成員變量m B1() //構(gòu)造函數(shù) {m=0;} }; classB2 //基類B2 { public: intm; //成員變量m B2() //構(gòu)造函數(shù) {m=100;} };54classD:publicB1,publicB2 //派生類

D { public: voidTest() { //cout<<m<<endl; //此語句將引起二義性錯誤

cout<<"B1::m="<<B1::m<<endl;//輸出基類B1中的成員m的值

cout<<"B2::m="<<B2::m<<endl;//輸出基類B2中的成員m的值 } };

intmain() //主函數(shù) {

Dd1; //派生類對象

d1.Test(); return0;}55B1::m=0B2::m=100程序的運行結(jié)果為:565.6.2名字覆蓋

當派生類中定義有與基類中同名的成員時,則從基類中繼承得到的成員被派生類的同名成員覆蓋,派生類對基類成員的直接訪問將被派生類中該成員取代,為訪問基類成員,必須采用成員名限定方法。57【例5.9】成員訪問沖突。

/*05_09.cpp*/#include<iostream> usingnamespacestd; classCircle //定義圓類 {

protected: floatradius; //半徑

public: Circle(floatr) //構(gòu)造函數(shù) { radius=r; } floatArea() //求圓面積 {return3.14f*radius*radius;} };58classCylinder:publicCircle //圓柱體派生類 {

private: floatheight; //高度

public: Cylinder(floatr,floath):Circle(r) //構(gòu)造函數(shù) { height=h; } floatArea() //求圓柱體面積,覆蓋了基類的Area()函數(shù) { floatbotarea=0,sidearea=0; botarea=Circle::Area()*2; //底面積*2

sidearea=2*3.14f*radius*height;//側(cè)面積

returnbotarea+sidearea; } floatVolume() //圓柱體積 { returnCircle::Area()*height;}//基類求面積乘高度 };59intmain() { Cylindercr1(10,5); //定義圓柱體對象

cout<<"BottomArea="<<cr1.Circle::Area()<<endl;//訪問基類成員

cout<<"Area="<<cr1.Area()<<endl; //訪問派生類成員

cout<<"Volume="<<cr1.Volume()<<endl; return0; }60BottomArea=314Area=942Volume=1570程序的運行結(jié)果為:61從上述程序可以看出,用派生類對象訪問與基類中同名的成員時,會調(diào)用本類中的成員,而不會訪問到基類成員。為訪問到基類的同名成員,需要以成員名限定的方法來指明,如在Cylinder類中的求面積和體積函數(shù)就用到了基類的Circle::Area()函數(shù)。另外,要注意的一點是,在派生類如果定義了和基類中同名的函數(shù),則基類中所有的同名的重載函數(shù)都將被覆蓋,即在派生類中或通過派生類對象都無法直接訪問基類的任何一個同名函數(shù),如圖5.7中定義了一個基類和派生類,62圖5.7派生類對基類的名字覆蓋

635.6.3賦值兼容規(guī)則

在派生類對象和基類對象之間賦值時需要注意賦值的方向,即這些賦值操作需要滿足賦值兼容規(guī)則。賦值兼容規(guī)則包括:(1)基類對象可以賦值給基類對象,也可以把派生類對象賦值給基類對象。(2)基類指針可以指向基類對象,也可以指向派生類對象。(3)基類引用可以指向基類對象,也可以指向派生類對象。64例如,有基類Base和其派生類Derived,可以定義相應的對象、指針:Baseb1;Base*pb;Derivedd1;根據(jù)賦值兼容規(guī)則,在基類Base對象可以出現(xiàn)的任何地方都可以用派生類Derived對象來替代。(1)派生類對象可以賦值給基類對象,即派生類對象中來自基類成員,逐個賦值給基類對象的成員: b1=d1;(2)派生類的對象也可以初始化基類對象的引用: Base&rb=d1;(3)基類的指針賦值為派生類對象的地址: pb=&d1;65【例5.10】賦值兼容實例。

/*05_10.cpp*/#include<iostream> usingnamespacestd; classBase //基類Base { protected: intmember; public: Base() {member=0;} voidShow() //共有成員函數(shù) {cout<<"Base::Show():"<<member<<endl;} }; classDerived1:publicBase //第1個派生類Derived1 { public: Derived1(inta) {member=a;} voidShow() //重寫共有成員函數(shù)Show {cout<<"Derived1::Show():"<<member<<endl;} };66classDerived2:publicDerived1//第2個派生類Derived2 { public: Derived2(inta):Derived1(a) {} voidShow() //重寫共有成員函數(shù)Show {cout<<"Derived2::Show():"<<member<<endl;} }; voidTest(Base*pb) //測試函數(shù),用基類指針作參數(shù) {pb->Show();} voidTest(Base&br) //測試函數(shù),用基類引用作參數(shù) {br.Show();} intmain() //主函數(shù) {

Baseb0; //基類Base對象

Derived1d1(5); //派生類Derived1的對象

Derived2d2(10); //派生類Derived2的對象

Base*pb0; //基類指針pb0 pb0=&b0; //基類指針pb0指向基類對象b0 Test(pb0); b0=d1; //基類對象賦值為子類對象

Test(pb0); //測試輸出

pb0=&d1; //基類指針pb0指向基第一派生類Derived1的對象d1 Test(pb0); Test(d2); //第2派生類Derived2的對象d2的引用作參數(shù)傳給Test函數(shù)

return0; }67Base::Show():0Base::Show():5Base::Show():5Base::Show():10程序的運行結(jié)果為:685.6虛基類

5.6.1提出問題

在多繼承關(guān)系中,如果某個派生類D的多個基類(如類Bl和B2)派生自另一個公共基類B0,則在派生類對象中,會通過不同的繼承路徑多次得到基類B0的成員,即同時存在多份基類B0的成員。通過派生類D的對象訪問這些成員時,會出現(xiàn)對這些成員的訪問沖突。為解決沖突問題可以使用成員名限定的方法來唯一標識某個成員所屬的基類,但是這不能從根本上解決問題:派生類對象中存在基類成員的多個副本,如圖5.8所示。69圖5.8多繼承關(guān)系及派生類的成員構(gòu)成UML圖

705.6.2虛基類的概念

為使得公共基類B0在派生類D0中只產(chǎn)生一份基類成員,則需要將這個共同基類B0設(shè)置為虛基類,讓基類Bl和B2從基類B0虛擬繼承,這時從不同的路徑繼承過來的同名數(shù)據(jù)成員在派生類中就只有一個副本。同一個函數(shù)名也只有一個映射。這樣就解決了同名成員的唯一標識問題。使用虛基類,可以使公共基類的成員在其間接派生類中只保留一份。使用虛基類后,4個類之間的關(guān)系如圖5.9a所示,這時派生類中的成員如圖5.9b所示71圖5.9虛基類多繼承關(guān)系及派生類的成員構(gòu)成UML圖

72定義虛基類的格式如下:class派生類名:virtual繼承方式

基類名稱{

……};73在圖5.9中的幾個類可以寫成如下形式:classB0{public: intnv0;};classB1:virtualpublicB0{public: intnv1;};classB2:virtualpublicB0{public: intnv2;};classD0:publicB1,publicB2{public: intnv3;};

745.6.3虛基類的初始化

關(guān)于虛基類的初始化,有如下兩條規(guī)則:(1)所有從虛基類直接或者間接派生的類必須在該類構(gòu)造函數(shù)的成員初始化列表列出對虛基類構(gòu)造函數(shù)的調(diào)用,但是只有實際構(gòu)造對象的類的構(gòu)造函數(shù)才會引發(fā)對虛基類構(gòu)造函數(shù)的調(diào)用,而其他基類在成員初始化列表中對虛基類構(gòu)造函數(shù)的調(diào)用都會被忽略,從而保證了派生類對象中虛基類成員只會被初始化一次。(2)若某類構(gòu)造函數(shù)的成員初始化列表中同時列出對虛基類構(gòu)造函數(shù)和非虛基類構(gòu)造函數(shù)的調(diào)用,則會優(yōu)先執(zhí)行虛基類的構(gòu)造函數(shù)

75【例5.11】

設(shè)置虛基類以解決二義性

/*05-11.cpp*/ #include<iostream> usingnamespacestd; classBase //虛基類

溫馨提示

  • 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

提交評論