第7章繼承與派生_第1頁(yè)
第7章繼承與派生_第2頁(yè)
第7章繼承與派生_第3頁(yè)
第7章繼承與派生_第4頁(yè)
第7章繼承與派生_第5頁(yè)
已閱讀5頁(yè),還剩40頁(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)介

第7章繼承與派生主要內(nèi)容繼承與派生概念與使用方法運(yùn)用繼承機(jī)制對(duì)現(xiàn)有的類(lèi)進(jìn)行重用繼承中的構(gòu)造函數(shù)與析構(gòu)函數(shù)的調(diào)用順序?yàn)榕缮?lèi)設(shè)計(jì)合適的構(gòu)造函數(shù)初始化派生類(lèi)多繼承時(shí)的二義性問(wèn)題虛基類(lèi)7.1繼承與派生

已有的類(lèi)新的類(lèi)新類(lèi)將擁有原有類(lèi)的全部特性

基類(lèi)(Baseclass)/父類(lèi)(Superclass)派生類(lèi)(Derivedclass)/子類(lèi)(Subclass)派生繼承單繼承/單向繼承:每一個(gè)派生類(lèi)都有且僅有一個(gè)基類(lèi),派生類(lèi)可以看作是基類(lèi)的特例,它增加了某些基類(lèi)所沒(méi)有的性質(zhì)多繼承/多重繼承:與之相類(lèi)似,如果一個(gè)派生類(lèi)有兩個(gè)或兩個(gè)以上的基類(lèi),則稱(chēng)為多層派生/多層繼承:派生類(lèi)又作為基類(lèi),繼續(xù)派生新的類(lèi)A繼承的概念

class派生類(lèi)名:繼承方式1基類(lèi)名1,繼承方式2基類(lèi)名2,…{private:

私有數(shù)據(jù)和函數(shù)

public:

公有數(shù)據(jù)和函數(shù)

protected:

保護(hù)數(shù)據(jù)和函數(shù)};B派生類(lèi)實(shí)現(xiàn)1.派生類(lèi)的定義基類(lèi)名表:“繼承方式1基類(lèi)名1,繼承方式2基類(lèi)名2,…”,表示當(dāng)前定義的派生類(lèi)的各個(gè)基類(lèi)單繼承;多繼承繼承方式指定了派生類(lèi)成員以及類(lèi)外對(duì)象對(duì)于從基類(lèi)繼承來(lái)的成員的訪問(wèn)權(quán)限繼承方式有三種:public:公有繼承

private:私有繼承

protected:保護(hù)繼承classClock{private:intH,M,S;public:voidSetTime(intH=0,intM=0,intS=0); voidShowTime(); Clock(intH=0,intM=0,intS=0); ~Clock();};classAlarmClock:publicClock{private:intAH,AM;//響鈴的時(shí)間boolOpenAlarm;//是否關(guān)閉鬧鐘public:SetAlarm(intAH,intAM);//設(shè)置響鈴時(shí)間SwitchAlarm(boolOpen=true);//打開(kāi)/關(guān)閉鬧鈴ShowTime();//顯示當(dāng)前時(shí)間與鬧鈴時(shí)間}繼承方式,系統(tǒng)默認(rèn)為私有繼承【例如】ClockAlarmClock:類(lèi)名成員名AlarmClock::Clock::H,M,SSetTime()ShowTime()AH,AM,OpenAlarmSetAlarm()SwitchAlarm()ShowTime()AlarmClock()派生類(lèi)AlarmClock的成員構(gòu)成圖(表)2.派生類(lèi)的實(shí)現(xiàn)方式

(1)吸收基類(lèi)成員

基類(lèi)的全部成員被派生類(lèi)繼承,作為派生類(lèi)成員的一部分。如:Clock類(lèi)H、M、S,SetTime()、ShowTime()AlarmClock(2)改造基類(lèi)成員派生類(lèi)對(duì)繼承自基類(lèi)的某些成員進(jìn)行限制和改造。對(duì)基類(lèi)成員的訪問(wèn)限制主要通過(guò)繼承方式來(lái)實(shí)現(xiàn);對(duì)基類(lèi)成員的改造主要通過(guò)同名覆蓋來(lái)實(shí)現(xiàn)派生類(lèi)中的新成員“覆蓋”基類(lèi)的同名成員。名成員函數(shù)更小的作用域。如:ShowTime()(3)添加新成員派生類(lèi)增加新的數(shù)據(jù)成員和函數(shù)成員如:AlarmClock:AH、AM、OpenAlarm,SetAlarm()、SwitchAlarm()。3.繼承的性質(zhì)

(1)繼承關(guān)系是可以傳遞的一個(gè)基類(lèi)多個(gè)派生類(lèi)新的派生類(lèi)類(lèi)族

在類(lèi)族中,直接派生出某類(lèi)的基類(lèi)稱(chēng)為直接基類(lèi),基類(lèi)的基類(lèi)甚至更高層的基類(lèi)稱(chēng)為間接基類(lèi),如類(lèi)A類(lèi)B,類(lèi)B類(lèi)C,類(lèi)B是類(lèi)C的直接基類(lèi),類(lèi)A是類(lèi)B的直接基類(lèi),而類(lèi)A稱(chēng)為類(lèi)C的間接基類(lèi)。(2)繼承關(guān)系不允許循環(huán)不允許類(lèi)A類(lèi)B,類(lèi)B類(lèi)C,而類(lèi)C又繼承自類(lèi)A。C繼承與組合

繼承-------一般類(lèi)與特殊類(lèi)的關(guān)系,“isakindof”即AisakindofB,BA,如交通工具(vehicle)汽車(chē)(automobile)->小汽車(chē)(car)

組合--------整體與部分的關(guān)系,“isapartof”即AisapartofB,則允許A和其他數(shù)據(jù)成員組合為B,如:發(fā)動(dòng)機(jī)、車(chē)輪、電池、車(chē)門(mén)、方向盤(pán)、底盤(pán)小汽車(chē)

繼承和組合既有區(qū)別,也有聯(lián)系,某些復(fù)雜的類(lèi),需要二者一起使用。在多繼承時(shí),派生類(lèi)實(shí)際上是所有基類(lèi)屬性和行為的組合,派生類(lèi)也可以通過(guò)組合類(lèi)實(shí)現(xiàn)如:AlarmClock類(lèi)可以通過(guò)組合Clock類(lèi)實(shí)現(xiàn),從功能上講,基本的時(shí)鐘功能是鬧鐘功能的一部分

7.2繼承的方式

①公有成員公有成員

②私有成員×

③保護(hù)成員保護(hù)成員,可以通過(guò)派生類(lèi)的成員函數(shù)訪問(wèn),但不能由派生類(lèi)的對(duì)象直接訪問(wèn)A公有繼承特點(diǎn):

注意:對(duì)基類(lèi)成員的訪問(wèn),區(qū)別派生類(lèi)對(duì)象訪問(wèn)vs

派生類(lèi)成員函數(shù)訪問(wèn)

【例7-1】公有繼承及其訪問(wèn)將點(diǎn)理解為半徑長(zhǎng)度為0的圓,Point(點(diǎn))類(lèi)公有派生出新的Circle(圓)類(lèi)。圓類(lèi)具備Point類(lèi)的全部特征,同時(shí)自身也有自己的特點(diǎn):圓有半徑。123456789101112131415161718192021//Point.h#include<iostream>usingnamespacestd;classPoint{private: intX,Y;public: Point(intX=0,intY=0) {this->X=X,this->Y=Y; } voidmove(intOffX,intOffY) {X+=OffX,Y+=OffY; }voidShowXY() { cout<<"("<<X<<","<<Y<<")"<<endl; }};1234567891011121314151617181920212223242526/*******************************Circle.h**從Point類(lèi)派生出圓類(lèi)(Circle)********************************/#include"point.h"constdoublePI=3.14159;classCircle:publicPoint{private:doubleradius;//半徑public:

Circle(doubleR,intX,intY):Point(X,Y){ radius=R;}doublearea()//求面積{returnPI*radius*radius;}

voidShowCircle(){cout<<"Centreofcircle:";ShowXY();cout<<"radius:"<<radius<<endl;}};類(lèi)名成員名訪問(wèn)權(quán)限CirclePoint::X,Yprivate不可訪問(wèn)move()publicpublicShowXY()publicpublicradiusprivatearea()publicShowCircle()publicCircle()public313233343536373839404142434445/***********************p7_1.cpp**Circle類(lèi)的使用************************/#include"Circle.h"usingnamespacestd;intmain(){CircleCir1(100,200,10); Cir1.ShowCircle();cout<<"areais:"<<Cir1.area()<<endl; Cir1.move(10,20);Cir1.ShowXY();return0;}運(yùn)行結(jié)果Centreofcircle:(200,10)

radius:100

areais:31415.9

(210,30)

1234567891011121314151617181920212223242526/*******************************Circle.h**從Point類(lèi)派生出圓類(lèi)(Circle)********************************/#include"point.h"constdoublePI=3.14159;classCircle:publicPoint{private: doubleradius;//半徑public:

Circle(doubleR,intX,intY):Point(X,Y){ radius=R;}

doublearea()//求面積{ returnPI*radius*radius;}

voidShowCircle(){cout<<"Centreofcircle:";ShowXY();cout<<"radius:"<<radius<<endl;}};程序解釋派生類(lèi)Circle繼承了Point類(lèi)的除構(gòu)造函數(shù)外的全部成員,擁有從基類(lèi)繼承過(guò)來(lái)的成員與派生類(lèi)新添加的成員的總和。繼承方式為公有繼承,這時(shí),基類(lèi)中的公有成員在派生類(lèi)中訪問(wèn)屬性保持原樣,派生類(lèi)的成員函數(shù)及對(duì)象可以訪問(wèn)基類(lèi)派生的的公有成員?;?lèi)原有的外部接口(公有成員函數(shù)),如ShowXY()和move()變成了派生類(lèi)外部接口的一部分。在Circle的構(gòu)造函數(shù)中,為了給從基類(lèi)繼承來(lái)的數(shù)據(jù)成員賦初值,使用了初始化列表,其格式與組合類(lèi)相同B私有繼承

①公有成員,保護(hù)成員私有成員,派生類(lèi)的其他成員函數(shù)可以直接訪問(wèn)它們,類(lèi)外部,不能通過(guò)派生類(lèi)的對(duì)象訪問(wèn)它們

②私有成員×

③所有成員私有成員或不可訪問(wèn)的成員如果進(jìn)一步派生的,基類(lèi)的全部成員將無(wú)法在新的派生類(lèi)中被訪問(wèn)。中止了基類(lèi)的繼續(xù)派生,因此,一般情況下私有繼承的使用比較少特點(diǎn):

【例7-2】私有繼承派生類(lèi)的實(shí)現(xiàn)及其訪問(wèn)//Circle2.h#include"point.h"constdoublePI=3.14159;classCircle:privatePoint{private: doubleradius;//半徑public: Circle(doubleR,intX,intY); doublearea();//求面積 voidShowCircle();voidmove(intOffX,intOffY){point::move(OffX,OffY);}};類(lèi)名成員名訪問(wèn)權(quán)限CirclePoint::X,Yprivate不可訪問(wèn)move()publicprivateShowXY()publicprivateradiusprivatearea()publicShowCircle()publicCircle()public運(yùn)行結(jié)果Centreofcircle:(200,10)

radius:100

areais:31415.9

#include"Circle2.h"usingnamespacestd;intmain(){ CircleCir1(100,200,10); Cir1.ShowCircle(); cout<<"areais:"<<Cir1.area()<<endl; Cir1.move(10,20);//同名覆蓋//Cir1.ShowXY();//錯(cuò)誤,ShowXY()繼承為私有成員函數(shù)return0;}程序解釋由于是私有繼承,所有成員私有成員,派生類(lèi)對(duì)象不能直接訪問(wèn)任何一個(gè)基類(lèi)的成員。類(lèi)Circle的對(duì)象Cir1調(diào)用的都是派生類(lèi)自身的公有成員體現(xiàn)封裝性的優(yōu)越性,可重用與可擴(kuò)充性C保護(hù)繼承

①公有成員,保護(hù)成員保護(hù)成員。

②私有成員×直接訪問(wèn)修改Circle2.h,將派生類(lèi)的繼承方式改為保護(hù)繼承,其它部分不變:特點(diǎn):

//circle3.h#include“piont.h”classCircle:protectedpoint{//類(lèi)成員定義}類(lèi)名成員名訪問(wèn)權(quán)限CirclePoint::X,Yprivate不可訪問(wèn)move()publicprotectedShowXY()publicprotectedradiusprivatearea()publicShowCircle()publicCircle()public運(yùn)行結(jié)果Centreofcircle:(100,200)

radius:10

areais:31415.9

#include"Circle3.h"usingnamespacestd;intmain(){ CircleCir1(100,200,10); Cir1.ShowCircle(); cout<<"areais:"<<Cir1.area()<<endl; Cir1.move(10,20);//同名覆蓋//Cir1.ShowXY();//錯(cuò)誤,ShowXY()繼承為保護(hù)成員函數(shù)return0;}程序解釋:private、protected兩種繼承方式下,基類(lèi)所有成員在派生類(lèi)中的訪問(wèn)屬性都是完全相同的。即在派生類(lèi)中可以訪問(wèn)基類(lèi)的公有、保護(hù)成員,不可訪問(wèn)基類(lèi)的私有成員

如果將派生類(lèi)作為新的基類(lèi)繼續(xù)派生時(shí),private、protected兩種繼承方式區(qū)別就出現(xiàn)了。假設(shè)類(lèi)B以私有方式繼承自類(lèi)A,則無(wú)論B類(lèi)以什么方式派生出類(lèi)C,類(lèi)C的成員和對(duì)象都不能訪問(wèn)間接從A類(lèi)中繼承來(lái)的成員。如果類(lèi)B是以保護(hù)方式繼承自類(lèi)A,那么類(lèi)A中的公有和保護(hù)成員在類(lèi)B中都是保護(hù)成員。類(lèi)B再派生出類(lèi)C后,如果是公有派生或保護(hù)派生,則類(lèi)A中的公有和保護(hù)成員被類(lèi)C間接繼承后,類(lèi)C的成員函數(shù)可以訪問(wèn)間接從類(lèi)A中繼承來(lái)的成員。即類(lèi)A的成員可以沿繼承樹(shù)繼續(xù)向下傳播。

【例7-2

】保護(hù)繼承與保護(hù)成員的訪問(wèn)修改例7-1,除將基類(lèi)Point的數(shù)據(jù)成員X和Y的訪問(wèn)屬性改為protected外,又增加了一個(gè)派生類(lèi):Cylinder(圓柱體)類(lèi)。Cylinder類(lèi)保護(hù)繼承自類(lèi)circle。程序?qū)崿F(xiàn)如下:

123456789101112131415161718192021//Point2.h#include<iostream>usingnamespacestd;classPoint{protected: intX,Y;public:

Point(intX=0,intY=0) {this->X=X,this->Y=Y; }

voidmove(intOffX,intOffY) {X+=OffX,Y+=OffY; }

voidShowXY() { cout<<"("<<X<<","<<Y<<")"<<endl; }};1234567891011121314151617181920212223242526272829303132/**********************************p7_2.cpp**從circle類(lèi)派生出圓柱類(lèi)(Cylinder)**********************************/#include"point2.h"constdoublePI=3.14159;classCircle:protectedPoint{protected: doubleradius;//半徑public:

Circle(doubleR,intX,intY):Point(X,Y) { radius=R; }

doublearea()//求面積 { returnPI*radius*radius;}

voidShowCircle() {cout<<"Centreofcircle:";ShowXY();cout<<"radius:"<<radius<<endl; }};classCylinder:protectedCircle{private: doubleheight;public:

Cylinder(intX,intY,doubleR,doubleH):Circle(R,X,Y)

333435363738394041424344454647484950515253545556 { height=H; }

doublearea() {return2*Circle::area()+2*PI*radius*height; }

doublevolume() { returnCircle::area()*height; }

voidShowCylinder() {ShowCircle();

cout<<"heightofcylinder:"<<height<<endl; }};voidmain(){CylinderCY(100,200,10,50); CY.ShowCylinder();

cout<<"totalarea:"<<CY.area()<<endl; cout<<"volume:"<<CY.volume();}運(yùn)行結(jié)果Centreofcircle:(100,200)

radius:10

heightofcylinder:50

totalarea:3769.11

volume:15707.9

1234567891011121314151617181920212223242526272829303132/**********************************p7_2.cpp**從circle類(lèi)派生出圓柱類(lèi)(Cylinder)**********************************/#include"point2.h"constdoublePI=3.14159;classCircle:protectedPoint{protected: doubleradius;//半徑public:

Circle(doubleR,intX,intY):Point(X,Y) { radius=R; }

doublearea()//求面積 { returnPI*radius*radius;}

voidShowCircle() {cout<<"Centreofcircle:";ShowXY();cout<<"radius:"<<radius<<endl; }};classCylinder:protectedCircle{private: doubleheight;public:

Cylinder(intX,intY,doubleR,doubleH):Circle(R,X,Y)

333435363738394041424344454647484950515253545556 { height=H; }

doublearea() {return2*Circle::area()+2*PI*radius*height; }

doublevolume() { returnCircle::area()*height; }

voidShowCylinder() {ShowCircle();

cout<<"heightofcylinder:"<<height<<endl; }};voidmain(){CylinderCY(100,200,10,50); CY.ShowCylinder();

cout<<"totalarea:"<<CY.area()<<endl; cout<<"volume:"<<CY.volume();}Circle保護(hù)繼承自類(lèi)Point,因此類(lèi)Circle為子類(lèi),類(lèi)Point為父類(lèi),對(duì)于該子類(lèi)來(lái)講,保護(hù)成員與公有成員具有相同的訪問(wèn)特性。所以派生類(lèi)的成員函數(shù)ShowCircle()可以訪問(wèn)基類(lèi)從基類(lèi)繼承而來(lái)的保護(hù)成員,當(dāng)然它也可以調(diào)用從基類(lèi)繼承來(lái)的公有成員函數(shù)ShowXY()。

類(lèi)Circle沿類(lèi)的繼承樹(shù)繼續(xù)派生出類(lèi)Cylinder,繼承方式依然為保護(hù)繼承,因此,在類(lèi)cylinder中,它間接從類(lèi)Point中繼承了四個(gè)保護(hù)成員:數(shù)據(jù)成員X、Y,以及成員函數(shù)move()、ShowXY();同時(shí)它也直接從其父類(lèi)Circle中繼承了3個(gè)類(lèi)成員:數(shù)據(jù)成員radius,成員函數(shù)ShowCircle()、area(),它們都以保護(hù)成員的身份出現(xiàn)在類(lèi)Cylinder中。因此,在類(lèi)Cylinder的成員函數(shù)ShowCylinder()中,不僅可以訪問(wèn)從父類(lèi)Circle中直接繼承來(lái)的成員函數(shù)ShowCircle(),而且可以訪問(wèn)沿繼承樹(shù)從基類(lèi)Point中間接繼承來(lái)的數(shù)據(jù)成員X和Y。

當(dāng)通過(guò)類(lèi)Cylinder的對(duì)象CY調(diào)用成員函數(shù)area()時(shí),由于派生類(lèi)Cylinder聲明了一個(gè)和其父類(lèi)circle成員同名的新成員area(),派生的新成員函數(shù)就覆蓋了外層父類(lèi)的同名成員函數(shù),C++利用同名覆蓋原則,自動(dòng)選擇調(diào)用類(lèi)Cylinder新增的成員函數(shù)area(),輸出圓柱體的總的表面積三種繼承方式下,基類(lèi)成員在派生類(lèi)中的訪問(wèn)控制屬性總結(jié):

基類(lèi)屬性繼承方式publicprotectedprivatepublicpublicprotected不可訪問(wèn)protectedprotectedprotected不可訪問(wèn)privateprivateprivate不可訪問(wèn)7.3派生類(lèi)的構(gòu)造與析構(gòu)

A.派生類(lèi)構(gòu)造函數(shù)的定義

派生類(lèi)名(參數(shù)總表):基類(lèi)名1(參數(shù)表1),...,基類(lèi)名m(參數(shù)表m),成員對(duì)象名1(成員對(duì)象參數(shù)表1),...,成員對(duì)象名n(成員對(duì)象參數(shù)表n){派生類(lèi)新增成員的初始化;}

基類(lèi)名1(參數(shù)表1),...,基類(lèi)名m(參數(shù)表m)稱(chēng)為基類(lèi)成員的初始化表。成員對(duì)象名1(成員對(duì)象參數(shù)表1),...,成員對(duì)象名n(成員對(duì)象參數(shù)表n)

為成員對(duì)象的初始化表。基類(lèi)成員的初始化表與成員對(duì)象的初始化表構(gòu)成派生類(lèi)構(gòu)造函數(shù)的初始化表。在派生類(lèi)構(gòu)造函數(shù)的參數(shù)總表中,需要給出基類(lèi)數(shù)據(jù)成員的初值、成員對(duì)象數(shù)據(jù)成員的初值、新增一般數(shù)據(jù)成員的初值。在參數(shù)總表之后,列出需要使用參數(shù)進(jìn)行初始化的基類(lèi)名、成員對(duì)象名及各自的參數(shù)表,各項(xiàng)之間使用逗號(hào)分隔?;?lèi)名、對(duì)象名之間的次序無(wú)關(guān)緊要,它們各自出現(xiàn)的順序可以是任意的。在生成派生類(lèi)對(duì)象時(shí),程序首先會(huì)使用這里列出的參數(shù),調(diào)用基類(lèi)和成員對(duì)象的構(gòu)造函數(shù)。。什么時(shí)候需要定義派生類(lèi)的構(gòu)造函數(shù)?

如果基類(lèi)定義了帶有形參表的構(gòu)造函數(shù)時(shí),派生類(lèi)就應(yīng)當(dāng)定義構(gòu)造函數(shù),提供一個(gè)將參數(shù)傳遞給基類(lèi)構(gòu)造函數(shù)的途徑,保證在基類(lèi)進(jìn)行初始化時(shí)能夠獲得必要的數(shù)據(jù)。①調(diào)用基類(lèi)構(gòu)造函數(shù);②調(diào)用內(nèi)嵌成員對(duì)象的構(gòu)造函數(shù),調(diào)用順序按照它們?cè)陬?lèi)中定義的順序。③派生類(lèi)自己的構(gòu)造函數(shù)。如果基類(lèi)沒(méi)有定義構(gòu)造函數(shù),派生類(lèi)也可以不定義構(gòu)造函數(shù),全部采用默認(rèn)的構(gòu)造函數(shù),這時(shí)新增成員的初始化工作可以用其他公有成員函數(shù)來(lái)完成。2單繼承的構(gòu)造與析構(gòu)單繼承時(shí),派生類(lèi)構(gòu)造函數(shù)調(diào)用的一般次序如下:④當(dāng)派生類(lèi)對(duì)象析構(gòu)時(shí),各析構(gòu)函數(shù)的調(diào)用順序正好相反。首先調(diào)用派生類(lèi)析構(gòu)函數(shù)(清理派生類(lèi)新增成員);然后調(diào)用派生類(lèi)成員對(duì)象析構(gòu)函數(shù)(清理派生類(lèi)新增的成員對(duì)象);最后調(diào)用基類(lèi)析構(gòu)函數(shù)(清理從基類(lèi)繼承來(lái)的基類(lèi)子對(duì)象)?!纠?-3】單繼承的構(gòu)造與析構(gòu)。為了說(shuō)明單繼承的構(gòu)造,由Point類(lèi)派生出Circle類(lèi),再由兩個(gè)同心Circle類(lèi)對(duì)象與高度height構(gòu)成空管Tube類(lèi)。構(gòu)成空管的兩個(gè)同心圓的外圓從Circle類(lèi)繼承,內(nèi)圓組合Circle類(lèi)對(duì)象InCircle。Tube類(lèi)的層次結(jié)構(gòu)圖如圖:123456789101112131415161718192021222324252627282930313233343536/*******************************p7_3.cpp**多層繼承的構(gòu)造函數(shù)與析構(gòu)函數(shù)*******************************/#include<iostream>usingnamespacestd;classPoint{private: intX,Y;public:

Point(intX=0,intY=0) {this->X=X,this->Y=Ycout<<"point("<<X<<","<<Y<<")constructing..."<<endl; }

~Point() {cout<<"point("<<X<<","<<Y<<")destructing..."<<endl; }};classCircle:protectedPoint{protected: doubleradius;//半徑public:Circle(doubleR=0,intX=0,intY=0):Point(X,Y) { radius=R; cout<<"circleconstructing,radius:"<<R<<endl; }

~Circle() { cout<<"circledestructing,radius:"<<radius<<endl; }};37383940414243444546474849505152535455565758classtube:protectedCircle{private:doubleheight;CircleInCircle;public:tube(doubleH,doubleR1,doubleR2=0,intX=0,intY=0):InCircle(R2,X,Y),Circle(R1,X,Y){height=H;cout<<"tubeconstructing,height:"<<H<<endl; }

~tube(){cout<<"tubedestructing,height:"<<height<<endl;}};intmain(){tubeTU(100,20,5);return0;}運(yùn)行結(jié)果point(0,0)constructing...

circleconstructing,radius:20

point(0,0)constructing...

circleconstructing,radius:5

tubeconstructing,height:100

tubedestructing,height:100

circledestructing,radius:5

point(0,0)destructing...

circledestructing,radius:20

point(0,0)destructing...

定義了一個(gè)派生類(lèi)Tube的對(duì)象TU,首先試圖調(diào)用類(lèi)Tube的構(gòu)造函數(shù);類(lèi)Tube是派生類(lèi),由基類(lèi)Circle派生,于是試圖調(diào)用Circle類(lèi)的構(gòu)造函數(shù);類(lèi)Circle的基類(lèi)是Point,沿繼承樹(shù)上溯至頂層基類(lèi)Point,調(diào)用Point類(lèi)的構(gòu)造函數(shù);Tube同時(shí)又是一個(gè)組合類(lèi),由對(duì)象InCircle組合而成,于是,再?gòu)捻攲踊?lèi)Point開(kāi)始,依次調(diào)用調(diào)用Point類(lèi)的構(gòu)造函數(shù)、Circle的構(gòu)造函數(shù)。當(dāng)退出主函數(shù)之前,程序沿繼承樹(shù)自底向上依次調(diào)用各類(lèi)的析構(gòu)函數(shù),其順序與構(gòu)造函數(shù)順序正好相反。在C++中,類(lèi)型兼容主要指以下三種情況:

①派生類(lèi)對(duì)象可以賦值給基類(lèi)對(duì)象。

②派生類(lèi)對(duì)象可以初始化基類(lèi)的引用。

③派生類(lèi)對(duì)象的地址可以賦給指向基類(lèi)的指針。7.4類(lèi)型兼容類(lèi)型兼容是指在公有派生的情況下,一個(gè)派生類(lèi)對(duì)象可以作為基類(lèi)的對(duì)象來(lái)使用。類(lèi)型兼容又稱(chēng)為類(lèi)型賦值兼容或類(lèi)型適應(yīng)。

【例7-4】演示類(lèi)的兼容性。前面我們定義了類(lèi)Point,它公有派生出類(lèi)Circle,后者進(jìn)一步公有派生出類(lèi)Cylinder。我們可以通過(guò)這個(gè)單繼承的例子來(lái)驗(yàn)證類(lèi)型兼容規(guī)則。運(yùn)行結(jié)果(1,1)

(20,20)

(300,300)

(300,300)

(20,20)

8.4類(lèi)型兼容123456789101112131415161718192021222324252627282930313233343536373839/***************************************p7_4.cpp**從circle類(lèi)公有派生出圓柱類(lèi)Cylinder**演示類(lèi)的兼容性*****************************************/#include"Circle.h"classCylinder:publicCircle{private: doubleheight;public:

Cylinder(intX,intY,doubleR,doubleH):Circle(X,Y,R) { height=H; }voidShowCylinder() {ShowCircle(); cout<<"heightofcylinder:"<<height<<endl; }};intmain(){PointP(1,1);//Point類(lèi)對(duì)象CircleCir(20,20,15.5);//Circle類(lèi)對(duì)象CylinderCY(300,300,15.5,50);//Cylinder類(lèi)對(duì)象Point*Pp;//point類(lèi)指針Pp=&P;//將基類(lèi)對(duì)象地址賦給指向基類(lèi)的指針Pp->ShowXY();Pp=&Cir;//將派生類(lèi)對(duì)象地址賦給指向基類(lèi)的指針Pp->ShowXY();Pp=&CY;//將派生類(lèi)對(duì)象地址賦給指向基類(lèi)的指針Pp->ShowXY();Circle&RC=CY;//Circle類(lèi)引用引用了派生類(lèi)Cylinder對(duì)象RC.ShowXY();P=Cir;//Circle類(lèi)對(duì)象賦值給基類(lèi)Point類(lèi)對(duì)象P.ShowXY();return0;}定義了Point類(lèi)型的指針Pp指向了Point類(lèi)對(duì)象指向了Circle類(lèi)對(duì)象指向了Cylinder類(lèi)對(duì)象Pp調(diào)用了Point類(lèi)的成員函數(shù)ShowXY(),顯示了Point類(lèi)對(duì)象的中心坐標(biāo)值。調(diào)用了Point類(lèi)的成員函數(shù)ShowXY(),顯示了Circle類(lèi)對(duì)象的中心坐標(biāo)值。調(diào)用了Point類(lèi)的成員函數(shù)ShowXY(),顯示了Cylinder類(lèi)對(duì)象的中心坐標(biāo)值。還可以將display()形參改為基類(lèi)指針:voiddisplay(Pointp){p.ShowXY();}intmain(){PointP(1,1);//Point類(lèi)對(duì)象CircleCir(20,20,15.5);//Circle類(lèi)對(duì)象CylinderCY(300,300,15.5,50);//Cylinder類(lèi)對(duì)象display(P);//顯示對(duì)象P的中心坐標(biāo)display(Cir);//顯示對(duì)象Cir的中心坐標(biāo)display(CY);//顯示對(duì)象CY的中心坐標(biāo)

return0;}voiddisplay(Point&p){p.ShowXY();}如將上述程序改為:可將display()的參數(shù)改為引用形式:voiddisplay(Point*p){p->ShowXY();}這樣,可以分別把基類(lèi)對(duì)象P、派生類(lèi)Circle的對(duì)象Cir和派生類(lèi)Cylinder的對(duì)象CY的地址作為實(shí)參傳給基類(lèi)類(lèi)型指針,由C++編譯器實(shí)現(xiàn)隱式的類(lèi)型轉(zhuǎn)換。根據(jù)C++類(lèi)型兼容規(guī)則,p可以引用任何point的公有派生類(lèi)對(duì)象。7.5多繼承

多繼承(multipleinheritance,MI)是指派生類(lèi)具有兩個(gè)或兩個(gè)以上的直接基類(lèi)(directclass)。多繼承時(shí)派生類(lèi)構(gòu)造函數(shù)執(zhí)行的一般次序如下:

①調(diào)用各基類(lèi)構(gòu)造函數(shù);各基類(lèi)構(gòu)造函數(shù)調(diào)用順序按照基類(lèi)被繼承時(shí)聲明的順序,從左向右依次進(jìn)行。

②調(diào)用內(nèi)嵌成員對(duì)象的構(gòu)造函數(shù);成員對(duì)象的構(gòu)造函數(shù)調(diào)用順序按照它們?cè)陬?lèi)中定義的順序依次進(jìn)行。

③調(diào)用派生類(lèi)的構(gòu)造函數(shù);A多繼承的構(gòu)造與析構(gòu)注意:在繼承層次圖中,處于同一層次的各基類(lèi)構(gòu)造函數(shù)的調(diào)用順序取決于定義該派生類(lèi)時(shí)所指定的各基類(lèi)的先后順序,與派生類(lèi)構(gòu)造函數(shù)定義時(shí)初始化表中所列的各基類(lèi)構(gòu)造函數(shù)的先后順序無(wú)關(guān)。對(duì)同一個(gè)基類(lèi),不允許直接繼承兩次。B二義性問(wèn)題一般來(lái)說(shuō),在派生類(lèi)中對(duì)于基類(lèi)成員的訪問(wèn)應(yīng)該是唯一的,但是,由于多繼承中派生類(lèi)擁有多個(gè)基類(lèi),如果多個(gè)基類(lèi)中擁有同名的成員,那么,派生類(lèi)在繼承各個(gè)基類(lèi)的成員之后,當(dāng)我們調(diào)用該派生類(lèi)成員時(shí),由于該成員標(biāo)識(shí)符不唯一,出現(xiàn)二義性,編譯器無(wú)法確定到底應(yīng)該選擇派生類(lèi)中的哪一個(gè)成員,這種由于多繼承而引起的對(duì)類(lèi)的某個(gè)成員訪問(wèn)出現(xiàn)不唯一的情況就稱(chēng)為二義性問(wèn)題?!纠?-5】多繼承的二義性。例如:我們可以定義一個(gè)小客車(chē)類(lèi)car和一個(gè)小貨車(chē)類(lèi)Wagon,它們共同派生出一個(gè)客貨兩用車(chē)類(lèi)StationWagon。StationWagon繼承了小客車(chē)的特征,有座位seat,可以載客;又繼承了小貨車(chē)的特征,有裝載車(chē)廂load,可以載貨。程序?qū)崿F(xiàn)如下:789101112131415161718192021classCar//小客車(chē)類(lèi){private:intpower;//馬力intseat;//座位public:

Car(intpower,intseat) { this->power=power,this->seat=seat; }

voidshow() { cout<<"carpower:"<<power<<"seat:"<<seat<<endl; }};222324252627282930313233343536373839404142434445464748495051525354555657classWagon//小貨車(chē)類(lèi){private:intpower;//馬力intload;//裝載量public:

Wagon(intpower,intload) { this->power=power,this->load=load; }

voidshow() {cout<<"wagonpower:"<<power<<"load:"<<load<<endl; }};classStationWagon:publicCar,publicWagon//客貨兩用車(chē)類(lèi){public:

StationWagon(intpower,intseat,intload):Wagon(power,load),

Car(power,seat) { }

voidShowSW(){cout<<"StationWagon:"<<endl;Car::show();Wagon::show();}};intmain(){StationWagonSW(105,3,8);//SW.show();//錯(cuò)誤,出現(xiàn)二義性SW.ShowSW();return0;}運(yùn)行結(jié)果StationWagon:

carpower:105seat:3

wagonpower:105load:8

小客車(chē)類(lèi)Car和小貨車(chē)類(lèi)Wagon共同派生出客貨兩用車(chē)類(lèi)StationWagon,后者繼承了前者的屬性power和行為show()。

當(dāng)通過(guò)StationWagon類(lèi)的對(duì)象SW訪問(wèn)show()時(shí),程序出現(xiàn)編譯錯(cuò)誤。這是因?yàn)榛?lèi)Car和Wagon各有一個(gè)成員函數(shù)show(),在其共同的派生類(lèi)StationWagon中就有兩個(gè)相同的成員函數(shù),而程序在調(diào)用時(shí)無(wú)法決定到底應(yīng)該選擇哪一個(gè)成員函數(shù)。(1)

成員名限定通過(guò)類(lèi)的作用域分辨符明確限定出現(xiàn)歧義的成員是繼承自哪一個(gè)基類(lèi)。例如:程序第47、48兩行使用了Car::show()與Wagon::show()來(lái)表明調(diào)用哪個(gè)類(lèi)的show().(2)成員重定義在派生類(lèi)中新增一個(gè)與基類(lèi)中成員相同的成員,由于同名覆蓋,程序?qū)⒆詣?dòng)選擇派生類(lèi)新增的成員。可以對(duì)派生類(lèi)StationWagon的ShowSW()改名為show()。這樣,類(lèi)StationWagon中的show()覆蓋了基類(lèi)中的兩個(gè)同名的show(),使用SW.show();時(shí)不會(huì)出現(xiàn)二義性問(wèn)題。通常有兩種方法可以解決:7.6虛基類(lèi)在多繼承中,在派生類(lèi)的對(duì)象中,同名數(shù)據(jù)成員在內(nèi)存中同時(shí)擁有多個(gè)拷貝,同一個(gè)成員函數(shù)會(huì)有多個(gè)映射,出現(xiàn)二義性,這種二義性為間接二義性。

【例8-6】多重繼承的間接二義性。假定類(lèi)Car、Wagon從共同的基類(lèi)Automobile(汽車(chē))派生出來(lái),程序如下:1234567891011121314151617181920/***************************p8_6.cpp**多繼承的二義性****************************/#include<iostream>usingnamespacestd;classAutomobile

//汽車(chē)類(lèi){private:intpower;//馬力public:

Automobile(intpower){this->power=power;}voidshow(){cout<<"power:"<<power;}};2223242526272829303132333435363738394041424344454647484950515253classCar:publicAutomobile//小客車(chē)類(lèi){private:intseat;//座位public:

Car(intpower,intseat):Automobile(power){this->seat=seat;}voidshow(){cout<<"car:";Automobile::show();cout<<"seat:"<<seat<<endl;}};classWagon:publicAutomobile

//小貨車(chē)類(lèi){private:intload;//裝載量public:

Wagon(intpower,intload):Automobile(power){this->load=load;}voidshow(){cout<<"wagon:";Automobile::show();cout<<"load:"<<load<<endl;}};5354555657585960616263646566676869707172classStationWagon:publicCar,publicWagon

//客貨兩用車(chē)類(lèi){public:

StationWagon(intCPower,intWPower,intseat,intload) :Wagon(WPower,load),Car(CPower,seat) { }

voidshow(){cout<<"StationWagon:"<<endl;Car::show();Wagon::show();}};intmain(){StationWagonSW(105,108,3,8);SW.show();return0;}運(yùn)行結(jié)果:StationWagon:

carpower:105seat:3

wagonpower:108load:8

一個(gè)StationWagon類(lèi)對(duì)象中,具有多個(gè)從不同途徑繼承來(lái)的同名的數(shù)據(jù)成員power。一方面占據(jù)了內(nèi)存空間,另一方面由于在內(nèi)存中有不同的拷貝而可能造成數(shù)據(jù)不一致。將car::power設(shè)成105,Wagon::power設(shè)成108,那么StationWagon的power值究竟應(yīng)為多少?A虛基類(lèi)的定義為了解決從不同途徑繼承來(lái)的同名的數(shù)據(jù)成員在內(nèi)存中有不同的拷貝造成數(shù)據(jù)不一致問(wèn)題,將共同基類(lèi)設(shè)置為虛基類(lèi)。這時(shí)從不同的路徑繼承過(guò)來(lái)的同名數(shù)據(jù)成員在內(nèi)存中就只有一個(gè)拷貝,同一個(gè)函數(shù)名也只有一個(gè)映射。這樣不僅就解決了二義性問(wèn)題,也節(jié)省了內(nèi)存,避免了數(shù)據(jù)不一致的問(wèn)題。虛基類(lèi)的定義是在融合在派生類(lèi)的定義過(guò)程中的,其定義格式如下:

class派生類(lèi)名:virtual繼承方式基類(lèi)名其中:virtual是關(guān)鍵字,聲明該基類(lèi)為派生類(lèi)的虛基類(lèi)。在多繼承情況下,虛基類(lèi)關(guān)鍵字的作用范圍和繼承方式關(guān)鍵字相同,只對(duì)緊跟其后的基類(lèi)起作用。聲明了虛基類(lèi)之后,虛基類(lèi)在進(jìn)一步派生過(guò)程中始終和派生類(lèi)一起,維護(hù)同一個(gè)基類(lèi)子對(duì)象的拷貝。使用虛基類(lèi),將程序p7_6.cpp修改如下:classCar:virtual

publicAutomobile//小客車(chē)類(lèi)classWagon:virtualpublicAutomobile//小貨車(chē)類(lèi)使用虛基類(lèi)后的繼承層次圖與類(lèi)成員圖如下:這時(shí),從Automobile中不同途徑繼承來(lái)的power、show()在StationWagon中只有一個(gè)拷貝。B虛基類(lèi)的構(gòu)造與析構(gòu)C++將建立對(duì)象時(shí)所使用的派生類(lèi)稱(chēng)為最遠(yuǎn)派生類(lèi)。對(duì)于虛基類(lèi)而言,由于最遠(yuǎn)派生類(lèi)對(duì)象中只有一個(gè)公共虛基類(lèi)子對(duì)象,為了初始化該公共基類(lèi)子對(duì)象,最遠(yuǎn)派生類(lèi)的構(gòu)造函數(shù)要調(diào)用該公共基類(lèi)的構(gòu)造函數(shù),而且只能被調(diào)用一次。C++同時(shí)規(guī)定,在初始化列表中同時(shí)出現(xiàn)對(duì)虛基類(lèi)和非虛基類(lèi)構(gòu)造函數(shù)的調(diào)用,虛基類(lèi)的構(gòu)造函數(shù)先于非虛基類(lèi)的構(gòu)造函數(shù)的執(zhí)行。虛基類(lèi)的析構(gòu)順序與構(gòu)造順序完全相反,最開(kāi)始析構(gòu)的是最遠(yuǎn)派生類(lèi)自身,最后析構(gòu)的是虛基類(lèi)。盡管從程序上看,虛基類(lèi)被析構(gòu)多次,實(shí)際上只有在最后一次被執(zhí)行,中間的全部被忽略。

虛基類(lèi)的構(gòu)造函數(shù)調(diào)用分三種情況:

(1)虛基類(lèi)沒(méi)有定義構(gòu)造函數(shù)程序自動(dòng)調(diào)用系統(tǒng)缺省的構(gòu)造函數(shù)來(lái)初始化派生類(lèi)對(duì)象中的虛基類(lèi)子對(duì)象。

(2)虛基類(lèi)定義了缺省構(gòu)造函數(shù)程序自動(dòng)調(diào)用自定義的缺省構(gòu)造函數(shù)和析構(gòu)函數(shù)。

(3)虛基類(lèi)定義了帶參數(shù)的構(gòu)造函數(shù)這種情況下,虛基類(lèi)的構(gòu)造函數(shù)調(diào)用相對(duì)比較復(fù)雜。因?yàn)樘摶?lèi)定義了帶參數(shù)的構(gòu)造函數(shù),所以在整個(gè)繼承結(jié)構(gòu)中,直接或間接繼承虛基類(lèi)的所有派生類(lèi),都必須在構(gòu)造函數(shù)的初始化表中列出對(duì)虛基類(lèi)的初始化。但是,只有用于建立派生類(lèi)對(duì)象的那個(gè)最遠(yuǎn)派生類(lèi)的構(gòu)造函數(shù)才調(diào)用虛基類(lèi)的構(gòu)造函數(shù),而派生類(lèi)的其它非虛基類(lèi)中所列出的對(duì)這個(gè)虛基類(lèi)的構(gòu)造函數(shù)的調(diào)用被忽略,從而保證對(duì)公共虛基類(lèi)子對(duì)象只初始化一次。將程序p7_6.cpp修改后,在編譯下列語(yǔ)句時(shí)顯示編譯錯(cuò)誤:StationWagon(intCPower,intWPower,intseat,intload) :Wagon(WPower,load),Car(CPower,seat)系統(tǒng)在調(diào)用StationWagon的構(gòu)造函數(shù)時(shí),首先調(diào)用虛基類(lèi)的構(gòu)造函數(shù),以便初始化虛基類(lèi)中的數(shù)據(jù)成員。由于在StationWagon的構(gòu)造函數(shù)中沒(méi)有列出基類(lèi)構(gòu)造函數(shù)的調(diào)用形式,系統(tǒng)調(diào)用虛基類(lèi)的默認(rèn)構(gòu)造函數(shù)Automobile()。但是,在類(lèi)Automobile中,默認(rèn)構(gòu)造函數(shù)被Automobile(int)取代,沒(méi)有Automobile()可調(diào)用,故而出錯(cuò)。一個(gè)避免出錯(cuò)的方法是將虛基類(lèi)的構(gòu)造函數(shù)Automobile(int)更改成帶默認(rèn)形參值的形式:Automobile(int=0),但是此時(shí)虛基類(lèi)中的數(shù)據(jù)成員無(wú)法初始化。為了是初始化虛基類(lèi)中的數(shù)據(jù)成員,需要在最遠(yuǎn)派生類(lèi)的構(gòu)造函數(shù)中定義對(duì)虛基類(lèi)構(gòu)造函數(shù)調(diào)用的初始化列表。

將程序p8_6.cpp修改如下123456789101112131415161718192021/***************************p8_7.cpp**虛基類(lèi)的構(gòu)造函數(shù)****************************/#include<iostream>usingnamespacestd;classAutomobile

//汽車(chē)類(lèi){private:intpower;//馬力public:

Automobile(intpower) { this->power=power; cout<<"

溫馨提示

  • 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)論