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

下載本文檔

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

文檔簡(jiǎn)介

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

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

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

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

圖5.4單繼承與多繼承

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

7前面的知識(shí)中,我們學(xué)習(xí)了如何定義類和如何實(shí)現(xiàn)類的抽象與封裝,通常在不同的類中,數(shù)據(jù)成員和函數(shù)成員都是不同的,但對(duì)于某些特定的問題,有時(shí)候兩個(gè)類的基本或大部分內(nèi)容是相同的,在圖5.2中給出了學(xué)生體系的概念,我們利用現(xiàn)有知識(shí)可以首先聲明了一類來描述學(xué)生這一基本概念,如下: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)在需要一個(gè)新類UGStudent來描述大學(xué)生概念,除上述的基本成員外,還需要用到年齡、年級(jí)等信息,我們可以如下定義此類: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ù)進(jìn)行測(cè)試:intmain(){ UGStudentst1(100,”wang”,18,1); st1.Print(); //調(diào)用基類的函數(shù)

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

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

145.3派生類的訪問屬性

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

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對(duì)于靜態(tài)成員來說,與普通成員函數(shù)組合,將產(chǎn)生以下兩種情況:(1)派生類中靜態(tài)函數(shù)對(duì)基類中靜態(tài)成員的訪問。(2)派生類的普通成員函數(shù)要訪問基類中的靜態(tài)成員。靜態(tài)成員的訪問控制變化完全遵循表5.1的規(guī)則,這兩種情況和派生類中普通成員函數(shù)訪問基類中普通成員沒有區(qū)別。

17【例5.2】公有繼承時(shí)的訪問控制權(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; //錯(cuò)誤,無法訪問基類的私有成員cout<<b<<endl; //正確,可以訪問基類的保護(hù)成員cout<<c<<endl; //正確,可以訪問基類的公有成員//fun1(); //錯(cuò)誤,無法訪問基類的私有成員fun2(); //正確,可以訪問基類的保護(hù)成員fun3(); //正確,可以訪問基類的公有成員seta(10); //正確,間接訪問基類成員acout<<d<<endl; //正確,可以訪問派生類的私有成員}};19intmain(){Baseb1(5,6,7);//cout<<b1.a; //錯(cuò)誤,無法訪問對(duì)象的私有成員//cout<<b1.b; //錯(cuò)誤,無法訪問對(duì)象的保護(hù)成員cout<<b1.c<<endl; //正確,可以訪問對(duì)象的公有成員cout<<b1.geta()<<endl; //正確,間接訪問對(duì)象的私有成員aSubs1(11,15,19,22);s1.test(); //正確,可以訪問對(duì)象的公有成員s1.c=200; //正確,可以訪問對(duì)象的公有成員s1.fun3(); //正確,可以訪問對(duì)象的公有成員return0;}205.4派生類的構(gòu)造函數(shù)和析構(gòu)函數(shù)

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

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

/*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程序的運(yùn)行結(jié)果為:24析構(gòu)函數(shù)與構(gòu)造函數(shù)執(zhí)行的順相反,將按如下順序執(zhí)行:(1)派生類的構(gòu)造函數(shù);(2)對(duì)象成員的構(gòu)造函數(shù)(如果有的話),有多個(gè)時(shí)與聲明的順序相反;(3)基類對(duì)象的析構(gòu)函數(shù)。25對(duì)例5.3中的程序進(jìn)行改造,為每一個(gè)類添加析構(gòu)函數(shù),然后再進(jìn)行測(cè)試。

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程序的運(yùn)行結(jié)果為:275.4.2派生類構(gòu)造函數(shù)和析構(gòu)函數(shù)的構(gòu)造規(guī)則

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

1.如基類中定義了默認(rèn)構(gòu)造函數(shù),且該默認(rèn)構(gòu)造函數(shù)能夠完成派生類對(duì)象中基類成員的構(gòu)造,則派生類構(gòu)造函數(shù)無需顯式調(diào)用基類構(gòu)造函數(shù),直接調(diào)用基類的默認(rèn)構(gòu)造函數(shù)即可,這是一種較為簡(jiǎn)單的情況,例5.3中的繼承就屬于此類情況,下面的例子也可以說明這一點(diǎn)。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程序的運(yùn)行結(jié)果為:322.若基類中定義了有參數(shù)的構(gòu)造函數(shù),或者所定義的默認(rèn)構(gòu)造函數(shù)不能完成基類成員的構(gòu)造,則必須通過派生類構(gòu)造函數(shù)顯式向調(diào)用基類的構(gòu)造函數(shù),向帶參數(shù)的構(gòu)造函數(shù)傳遞參數(shù),這需要用到“成員初始化列表”的語法。另外,對(duì)于派生類中普通數(shù)據(jù)成員的初始化,以及對(duì)象成員的構(gòu)造也可以放在成員初始化列表中完成。此時(shí)這些以逗號(hào)隔開的基類構(gòu)造函數(shù)調(diào)用數(shù)據(jù)成員初始化和構(gòu)造的放置順序可以是任意的。因此派生類的構(gòu)造函數(shù)定義一般格式如下:派生類名(參數(shù)列表):基類構(gòu)造函數(shù)(參數(shù)列表1),子對(duì)象成員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 //定義學(xué)生基類 {

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

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

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

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

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

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; //對(duì)象成員 };36//主函數(shù)

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

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程序的運(yùn)行結(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)……,子對(duì)象名(參數(shù)表n)……{派生類構(gòu)造函數(shù)體;}42多繼承下派生類的構(gòu)造函數(shù)與單繼承下派生類構(gòu)造函數(shù)相似,它必須同時(shí)負(fù)責(zé)該派生類所有基類構(gòu)造函數(shù)的調(diào)用同時(shí),派生類的參數(shù)個(gè)數(shù)必須包含完成所有基類初始化所需的參數(shù)個(gè)數(shù)。派生類構(gòu)造函數(shù)執(zhí)行順序是先執(zhí)行所有基類的構(gòu)造函數(shù),再執(zhí)行派生類本身構(gòu)造函數(shù)。處于同一層次的各基類構(gòu)造函數(shù)的執(zhí)行順序取決于聲明派生類時(shí)所指定的各基類順序,與派生類構(gòu)造函數(shù)中所定義的成員初始化列表的各項(xiàng)順序無關(guān)。另外,析構(gòu)函數(shù)的調(diào)用順序則與構(gòu)造函數(shù)完全相反。43【例5.6】多繼承舉例。

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

classGStudent { protected: intnumber; //學(xué)號(hào)

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//在職研究生類,從兩個(gè)基類派生

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

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

若多個(gè)基類中定義有同名成員,則派生類對(duì)這些同名成員的訪問可能存在沖突。為避免可能出現(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ù)訪問不明確的錯(cuò)誤 mp4.MP3Player::Play(); mp4.VideoPlayer::Play(); return0; }51Playmp3music.Playvideo.程序的運(yùn)行結(jié)果為:52對(duì)于多繼承,如果在不同的基類中定義了同名的成員,在派生類中要區(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; //此語句將引起二義性錯(cuò)誤

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

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

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

Dd1; //派生類對(duì)象

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

當(dāng)派生類中定義有與基類中同名的成員時(shí),則從基類中繼承得到的成員被派生類的同名成員覆蓋,派生類對(duì)基類成員的直接訪問將被派生類中該成員取代,為訪問基類成員,必須采用成員名限定方法。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); //定義圓柱體對(duì)象

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

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

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

635.6.3賦值兼容規(guī)則

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

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

Baseb0; //基類Base對(duì)象

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

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

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

Test(pb0); //測(cè)試輸出

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

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

5.6.1提出問題

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

705.6.2虛基類的概念

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

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

基類名稱{

……};73在圖5.9中的幾個(gè)類可以寫成如下形式: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ù)的成員初始化列表列出對(duì)虛基類構(gòu)造函數(shù)的調(diào)用,但是只有實(shí)際構(gòu)造對(duì)象的類的構(gòu)造函數(shù)才會(huì)引發(fā)對(duì)虛基類構(gòu)造函數(shù)的調(diào)用,而其他基類在成員初始化列表中對(duì)虛基類構(gòu)造函數(shù)的調(diào)用都會(huì)被忽略,從而保證了派生類對(duì)象中虛基類成員只會(huì)被初始化一次。(2)若某類構(gòu)造函數(shù)的成員初始化列表中同時(shí)列出對(duì)虛基類構(gòu)造函數(shù)和非虛基類構(gòu)造函數(shù)的調(diào)用,則會(huì)優(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等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(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)論