第6章類的繼承_第1頁
第6章類的繼承_第2頁
第6章類的繼承_第3頁
第6章類的繼承_第4頁
第6章類的繼承_第5頁
已閱讀5頁,還剩43頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

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

文檔簡介

第6章類的繼承C++高級程序設(shè)計(jì)

第6章類的繼承

繼承(Inheritance)是C++語言的重要機(jī)制,是面向?qū)ο蟪绦蛟O(shè)計(jì)方法的三個基本特征之一。繼承允許程序在已有類的基礎(chǔ)上進(jìn)行擴(kuò)展,是一種重要的代碼復(fù)用手段。繼承反映了類與類之間的一種層次關(guān)系,更是現(xiàn)實(shí)世界中事物之間存在的復(fù)雜聯(lián)系的體現(xiàn)。繼承也體現(xiàn)了人類認(rèn)識事物由簡單到復(fù)雜的過程和思考問題的方法。本章著重學(xué)習(xí)繼承這一面向?qū)ο蟪绦蛟O(shè)計(jì)的重要概念,以及在C++語言中實(shí)現(xiàn)繼承的相關(guān)技術(shù)。6.1面向?qū)ο缶幊獭^承:概念與思想

6.2

派生類:定義及相關(guān)技術(shù)

6.3派生類的構(gòu)造與析構(gòu)6.4多重繼承與虛基類

6.5

案例實(shí)訓(xùn)

第6章類的繼承6.1面向?qū)ο缶幊獭^承:概念與思想類的繼承機(jī)制是指類可以在已定義類的基礎(chǔ)上派生出新類,新類將擁有原有類的數(shù)據(jù)和函數(shù),并且可以增添新的數(shù)據(jù)和函數(shù)成員,或者對原有類中的成員進(jìn)行更新。

在面向?qū)ο缶幊讨?,原有類被稱為基類(BaseClass)或父類(SuperClass),新產(chǎn)生的類被稱為派生類(DerivedClass)或子類(SubClass)。

例如,由學(xué)生類可以派生出中學(xué)生類、大學(xué)生類和研究生類。由交通工具類可派生出汽車類、輪船類和飛機(jī)類,而汽車類又可充當(dāng)客車類和卡車類的基類。

類的繼承有兩種方式:單繼承和多重繼承。單繼承的派生類有且僅有一個直接基類,如圖6-1中的飛機(jī)類是單繼承于交通工具類,戰(zhàn)斗機(jī)類則是單繼承于飛機(jī)類。在單繼承中,可以視派生類是基類的特例。多重繼承的派生類具有兩個或兩個以上的直接基類,如圖6-1中的水陸兩用車類繼承于客車類和客船類。在多重繼承中,派生類是從多個基類派生而來,是一個具有多個基類特征的復(fù)合體,就像雜交水稻具有不同稻種特性一樣。6.1面向?qū)ο缶幊獭^承:概念與思想6.1面向?qū)ο缶幊獭^承:概念與思想圖6-1類的單繼承和多重繼承6.1面向?qū)ο缶幊獭^承:概念與思想繼承與組合都屬于面向?qū)ο蟮拇a復(fù)用技術(shù)。繼承和組合既有區(qū)別,也有聯(lián)系。在一些復(fù)雜的類的設(shè)計(jì)中,二者經(jīng)常是一起使用。在某些情況下,繼承和組合的實(shí)現(xiàn)方法還可以互換。例如,圓(Circle)類的設(shè)計(jì),其圓心用點(diǎn)(Point)類來描述,此時(shí),即可以以Point類為父類設(shè)計(jì)Circle類,使其擁有圓心坐標(biāo),也可以在Circle類中用Point類定義數(shù)據(jù)成員center。代碼框架如下:classPoint{//點(diǎn)類private: doublex,y;public:...};classCircle{//組合 private: Pointcenter; doubleradius; public: ...}; classCircle:publicPoint{//繼承private: doubleradius;public:...};6.1面向?qū)ο缶幊獭^承:概念與思想6.2派生類

:定義及相關(guān)技術(shù)派生類繼承了基類的數(shù)據(jù)成員和函數(shù)成員。C++支持3種繼承方式,不同的繼承方式?jīng)Q定了基類中成員被派生類繼承后的可見性。派生類中可以重定義基類中的同名成員函數(shù)。派生類與基類屬于同一類族,它們之間存在賦值兼容問題。本節(jié)主要介紹派生類的定義、繼承方式、成員函數(shù)覆蓋和賦值兼容等基礎(chǔ)知識。6.2派生類:定義及相關(guān)技術(shù)6.2.1派生類的定義定義派生類的一般格式如下:class<派生類名>:[<繼承方式1>]<基類名1>,...,[<繼承方式n>][<基類名n>]{<派生類的成員>};其中:與類的定義相似,用關(guān)鍵字class標(biāo)明是類定義。區(qū)別在于派生類名后(用冒號分隔)列出所繼承的基類。對于單繼承只有一個基類,而多重繼承則有多個基類,它們之間用逗號分隔。繼承方式有3種:公有繼承(public)、私有繼承(private)和保護(hù)繼承(protected)。不指明繼承方式等同于私有繼承。派生類中的成員包括數(shù)據(jù)成員和成員函數(shù)。與普通類相同,派生類中數(shù)據(jù)成員的訪問控制限定通常是私有的,成員函數(shù)是公有的。6.2派生類:定義及相關(guān)技術(shù)【例6-1】派生類定義與派生類對象中的數(shù)據(jù)成員和成員函數(shù)。

運(yùn)行結(jié)果:baseObj:baseData=100derivedObj:baseData=500derivedData=600

6.2派生類:定義及相關(guān)技術(shù)跟蹤與觀察:(1) 從前面的知識可知,對象在邏輯上數(shù)據(jù)成員與函數(shù)成員具有封裝性,但在物理上其實(shí)是存儲在內(nèi)存的不同區(qū)域(堆棧區(qū)和代碼區(qū))。從圖6-2(a)可見,派生類對象derivedObj的數(shù)據(jù)部分含有兩部分?jǐn)?shù)據(jù)BaseClass和derivedData(其值為600),展開BaseClass項(xiàng),其中含有baseData(其值為500)。對象derivedObj自動包含了基類部分,并且派生類的構(gòu)造函數(shù)通過調(diào)用基類的構(gòu)造函數(shù)將500賦給了baseData。(2) 從圖6-2(b)可見,派生類對象derivedObj繼承了基類的公有函數(shù)show,DerivedClass::show的值是0x010a14b0,BaseClass::show(void)與BaseClass::show完全相同。

6.2派生類:定義及相關(guān)技術(shù)(b)圖6-2派生類對象擁有的數(shù)據(jù)成員和成員函數(shù)派生類繼承基類的成員函數(shù)也是自動的,系統(tǒng)自動將基類的成員函數(shù)show當(dāng)成派生類的成員函數(shù)show,但實(shí)際上兩者都是同一個函數(shù)voidBaseClass::show(void)。6.2派生類:定義及相關(guān)技術(shù)C++中下列特殊的成員函數(shù)不被派生類所繼承: ①構(gòu)造函數(shù); ②析構(gòu)函數(shù); ③私有函數(shù); ④賦值運(yùn)算符(=)重載函數(shù)。構(gòu)造函數(shù)、析構(gòu)函數(shù)和賦值運(yùn)算符重載函數(shù)不被繼承的主要原因是基類的對應(yīng)函數(shù)不能處理在派生類引入的新的數(shù)據(jù)成員,不能完全正確地完成相應(yīng)的功能(只能正確地處理基類的數(shù)據(jù)成員)。因而,派生類對象在調(diào)用這些函數(shù)時(shí)會首先調(diào)用基類的對應(yīng)函數(shù)。6.2派生類:定義及相關(guān)技術(shù)在派生類設(shè)計(jì)時(shí),需要注意下面幾點(diǎn):派生類吸收基類成員。派生類繼承吸收了基類的全部數(shù)據(jù)成員以及除了構(gòu)造函數(shù)、析構(gòu)函數(shù)、賦值和私有函數(shù)之外的全部函數(shù)成員。派生類修改基類成員。對繼承到派生類中基類成員的修改包括兩個方面:一是基類成員的訪問方式問題,這由派生類定義時(shí)的繼承方式來控制;二是對基類成員的覆蓋,也就是在派生類中定義了與基類中同名的數(shù)據(jù)成員或成員函數(shù),由于作用域不同,于是發(fā)生同名覆蓋(Override),基類中的成員就被替換成派生類中的同名成員。派生類增添新成員。在派生類中,除了從基類中繼承過來的成員外,還可以根據(jù)需要在派生類中添加新的數(shù)據(jù)成員和成員函數(shù),以此實(shí)現(xiàn)必要的新功能。在派生類中添加新成員是繼承和派生機(jī)制的核心,它保證了派生類是基類的擴(kuò)展。6.2派生類:定義及相關(guān)技術(shù)6.2.2繼承方式與訪問控制在類的定義中,用訪問控制符private、protected和public說明成員的可見性。公有成員能被任何函數(shù)所訪問,而私有和保護(hù)成員僅能接受類的成員函數(shù)和類的友元(包括友元函數(shù)和友元類)的訪問。派生類繼承于基類,那么基類成員對派生類函數(shù)的可見性如何呢?派生類在定義時(shí)需要指定繼承方式,所使用的關(guān)鍵字也是private、protected和public,它們分別對應(yīng)私有繼承、保護(hù)繼承和公有繼承3種方式。不同的繼承方式?jīng)Q定了基類中的成員在派生類中的可見性,表6-1列出了3種繼承方式在派生類中影響基類成員的可見性和訪問控制屬性的情況。6.2派生類:定義及相關(guān)技術(shù)表6-1派生類對基類成員的訪問能力

基類成員繼承方式privateprotectedpublicprivate不可訪問可訪問/私有成員可訪問/私有成員protected不可訪問可訪問/保護(hù)成員可訪問/保護(hù)成員public不可訪問可訪問/保護(hù)成員可訪問/公有成員6.2派生類:定義及相關(guān)技術(shù)表6-1中的“不可訪問”和“可訪問”表示在派生類中訪問基類成員的能力?!八接谐蓡T”、“保護(hù)成員”和“公有成員”表示基類成員在派生類中訪問控制屬性變化情況。例如,基類是public的成員采用private繼承方式時(shí),表中相應(yīng)項(xiàng)為“可訪問/私有成員”,表示在派生類中可直接訪問基類的公有成員,但該成員在派生類中其訪問控制屬性已被改為是私有成員,因此從派生類對象外不可直接訪問基類的公有成員(從基類對象外可以直接訪問)。派生類中基類成員訪問控制屬性的改變直接影響到基類成員在派生類對象外的訪問能力。6.2派生類:定義及相關(guān)技術(shù)從縱向觀察表6-1可知:基類的私有成員無論采用怎樣的繼承方式,它在派生類中均不可訪問,也不存在訪問控制屬性改變的問題?;愔械乃接谐蓡T只能通過保護(hù)或公有的函數(shù)訪問?;惖谋Wo(hù)成員在派生類中所有的繼承方式均可直接訪問,不過私有繼承方式會把基類訪問控制屬性是保護(hù)成員的轉(zhuǎn)換為派生類中的私有成員,而保護(hù)和公有繼承方式依然保持其為保護(hù)成員的訪問控制屬性?;惖墓谐蓡T在派生類中也是可直接訪問的,派生類中基類成員的訪問控制屬性均隨繼承方式而改變。從橫向觀察表6-1可知,3種繼承方式都不改變從派生類訪問基類成員的能力,但私有和保護(hù)繼承方式均會改變基類成員在派生類中的訪問控制屬性,而只有公有繼承方式保持基類成員的訪問控制屬性在派生類中不變。在應(yīng)用中,公有繼承方式絕對是主流的派生方式,其他兩種方式使用較少。6.2派生類:定義及相關(guān)技術(shù)如果對private、protected、public這3個關(guān)鍵字的訪問控制能力按從高到低的順序排序,則private最高,protected次之,而public最低。派生類中,基類成員的訪問控制屬性改變與否其實(shí)遵守如下準(zhǔn)則:“強(qiáng)者優(yōu)先”,即由基類成員在基類中的訪問控制符和派生類在繼承基類時(shí)所使用的繼承方式符(兩者中的“強(qiáng)者”)決定其在派生類中的訪問控制屬性?!纠?-2】3種繼承方式對訪問控制能力的影響示例。

6.2派生類:定義及相關(guān)技術(shù)程序說明:程序中定義了Base基類,其中含有私有的數(shù)據(jù)privateData和函數(shù)privateFunction,受保護(hù)的數(shù)據(jù)protectedData和函數(shù)protectedFunction,公有的數(shù)據(jù)publicData和函數(shù)publicFunction、show和構(gòu)造函數(shù)。在此基礎(chǔ)上,分別用3種繼承方式定義了派生類privateDerived、protedtedDerived和publicDerived。3個派生類的結(jié)構(gòu)十分相似,除構(gòu)造函數(shù)外,callBaseFunction函數(shù)調(diào)用Base類的4個成員函數(shù)。privateFunction在3個派生類中均不能被調(diào)用,而其他函數(shù)都可以。在程序的主函數(shù)中,定義了3個派生類的對象,并試圖通過這些對象調(diào)用基類的4個成員函數(shù)(privateFunction、protectedFunction、publicFunction和show),但僅有公有派生對象publicDerivedObj能調(diào)用公有函數(shù)publicFunction和show。

6.2派生類:定義及相關(guān)技術(shù)6.2.3成員函數(shù)的同名覆蓋與隱藏改造基類成員函數(shù)是派生類在基類上擴(kuò)展功能的重要手段之一。在派生類中重新定義基類的同名成員函數(shù)后,基類中的同名成員函數(shù)將被同名覆蓋(Override)或隱藏(Hide)。派生類中重定義的同名成員函數(shù)的函數(shù)簽名決定了基類中的成員函數(shù)是被同名覆蓋還是被隱藏。同名覆蓋是由于派生類與基類的同名成員函數(shù)的函數(shù)簽名相同,派生類對象在調(diào)用同名成員函數(shù)時(shí),系統(tǒng)調(diào)用派生類的同名成員函數(shù),而基類的相應(yīng)函數(shù)被遮蓋。隱藏是由于派生類與基類的同名成員函數(shù)的函數(shù)簽名不同(即函數(shù)名相同而形參不同),派生類對象在調(diào)用同名成員函數(shù)時(shí),系統(tǒng)只在派生類中查找,不再深入到基類。派生類的同名成員函數(shù)阻止了對基類中同名函數(shù)的訪問。6.2派生類:定義及相關(guān)技術(shù)編譯器調(diào)用類的成員函數(shù)的方法是:根據(jù)函數(shù)名(不是函數(shù)簽名)沿著類的繼承鏈逐級向上查找相匹配的函數(shù)定義。如果在類層次結(jié)構(gòu)的某個類中找到了同名的成員函數(shù),則停止查找,否則沿著繼承鏈向上繼續(xù)查找。派生中的同名函數(shù)阻止編譯器到其基類繼續(xù)查找,這就是出現(xiàn)同名覆蓋和隱藏現(xiàn)象的原因。從成員函數(shù)調(diào)用的查找方法可知,同名覆蓋和隱藏基類函數(shù)的原因是由于編譯器在派生類中遇到同名函數(shù)后不再到基類中繼續(xù)查找。然而,在派生類中可利用作用域標(biāo)識符(::)直接調(diào)用基類的同名成員函數(shù),方式如下:[<派生類對象名>.]<基類名>::<函數(shù)名>6.2派生類:定義及相關(guān)技術(shù)【例6-3】派生類中成員函數(shù)的同名覆蓋、隱藏和重載示例。6.2派生類:定義及相關(guān)技術(shù)6.2.4派生類與基類的賦值兼容由于派生類中包含從基類繼承的成員,因此在任何需要基類對象的地方都可以用公有派生類的對象來代替。派生類對象向基類對象、指針或引用賦值滿足以下兼容規(guī)則:派生類對象可以賦值給基類對象,它是把派生類對象中從對應(yīng)基類中繼承來的成員賦值給基類對象。派生類對象的地址可以賦給指向基類的指針變量,即基類指針可以指向派生類對象。但通過該指針只能訪問派生類中從基類繼承的成員,不能訪問派生類中的新增成員。派生類對象可以代替基類對象向基類對象的引用進(jìn)行賦值或初始化。但它只能引用包含在派生類對象中基類部分的成員。6.2派生類:定義及相關(guān)技術(shù)

在對基類對象進(jìn)行適當(dāng)?shù)霓D(zhuǎn)換或增添成員函數(shù)后,基類對象能向派生類對象、指針或引用賦值。由于從基類轉(zhuǎn)換來的派生類對象缺少派生類中新增的數(shù)據(jù)成員,因而訪問指向基類對象的派生類指針(或引用)是不安全的。下面列出兩種轉(zhuǎn)換方法。(1)在派生類中定義正確的轉(zhuǎn)換構(gòu)造函數(shù)或賦值運(yùn)算符重載函數(shù),則能確保將基類對象賦給派生類對象語句通過編譯。此時(shí),派生類對象中數(shù)據(jù)成員的內(nèi)容與所定義的構(gòu)造函數(shù)或賦值運(yùn)算符重載函數(shù)相關(guān)。6.2派生類:定義及相關(guān)技術(shù)(2)用強(qiáng)制類型轉(zhuǎn)換運(yùn)算符轉(zhuǎn)換基類對象為派生類對象并賦給派生類指針或引用,格式如下:<派生類對象指針>=static_cast<派生類*>(&<基類對象>);<派生類>&<派生類引用>=static_cast<派生類&>(<基類對象>);其中static_cast運(yùn)算符的使用格式為static_cast<類型名>(<表達(dá)式>),功能是把<表達(dá)式>轉(zhuǎn)換為<類型名>的類型。用static_cast運(yùn)算符能實(shí)現(xiàn)類層次結(jié)構(gòu)中基類(父類)和派生類(子類)之間指針或引用的轉(zhuǎn)換。這種轉(zhuǎn)換分為“上行”和“下行”兩種。注意:將強(qiáng)制類型轉(zhuǎn)換后的基類對象賦值給派生類的指針或引用,由于缺少派生類的成員,可能會導(dǎo)致程序崩潰。6.2派生類:定義及相關(guān)技術(shù)【例6-4】設(shè)計(jì)Person類和其派生類Student類,驗(yàn)證賦值兼容規(guī)則。

6.2派生類:定義及相關(guān)技術(shù)(1)在程序中,Student類是Person類的派生類,它在基類數(shù)據(jù)成員的基礎(chǔ)上增加了學(xué)號和入學(xué)成績數(shù)據(jù)成員。類中showInfo成員函數(shù)用于顯示對象中的數(shù)據(jù)。(2)主函數(shù)的最后一條語句studentPtr=&personObj;在編譯時(shí)出錯。錯誤信息為:errorC2440:“=”:無法從“Person*”轉(zhuǎn)換為“Student*”。說明基類對象地址直接賦給派生類指針不支持。如果改為studentPtr=static_cast<Student*>(&personObj);,則能通過編譯。(3)displayPersonInfo函數(shù)的形參為Person類的引用類型,在主程序中分別將基類對象personObj和派生類對象studentObj傳遞給該函數(shù),運(yùn)行結(jié)果顯示其只能調(diào)用基類中的成員函數(shù)showInfo,均只能見到Person類的部分?jǐn)?shù)據(jù)。(4)personObj=studentObj;語句的結(jié)果是personObj中數(shù)據(jù)成員的值和studentObj中基類部分?jǐn)?shù)據(jù)成員的值完全相同。6.3派生類的構(gòu)造與析構(gòu)在派生類對象中,數(shù)據(jù)成員分為兩類:一類是繼承于基類的數(shù)據(jù)成員;另一類是新添加的數(shù)據(jù)成員,包含其他類的成員對象。派生類對象中數(shù)據(jù)成員繼承了基類數(shù)據(jù),因而構(gòu)造函數(shù)需要負(fù)責(zé)它們的初始化。C++規(guī)定,基類成員的初始化工作由基類的構(gòu)造函數(shù)完成,而派生類的初始化工作由派生類的構(gòu)造函數(shù)完成。派生類構(gòu)造函數(shù)的定義格式如下:派生類名(<參數(shù)總表>):基類名1(<參數(shù)表1>)[,...,基類名m(<參數(shù)表m>),成員對象名1(<成員對象參數(shù)表1>),...,成員對象名n(<成員對象參數(shù)表n>)]{ <派生類新增成員的初始化>;}6.3派生類的構(gòu)造與析構(gòu)其中:(1)基類名1(<參數(shù)表1>),...,基類名m(<參數(shù)表m>)為基類成員的初始化表,成員對象名1(<成員對象參數(shù)表1>),...,成員對象名n(<成員對象參數(shù)表n>)為成員對象初始化表。派生類中新增的類型為基本類型的數(shù)據(jù)成員,也可采用成員對象的方式進(jìn)行初始化。(2)派生類構(gòu)造函數(shù)中所列出的基類名i(<參數(shù)表i>),在基類中需要有相匹配的構(gòu)造函數(shù)。<參數(shù)總表>中包含其所有基類、成員對象和新增成員初始化所需的參數(shù)。(3)冒號后面的基類名和對象名之間用逗號分隔,其順序沒有嚴(yán)格的限制。由于基類和派生類都需要調(diào)用構(gòu)造函數(shù)來實(shí)現(xiàn)初始化成員,這就產(chǎn)生了派生類構(gòu)造函數(shù)和析構(gòu)函數(shù)的執(zhí)行順序問題。在創(chuàng)建派生類對象時(shí),構(gòu)造函數(shù)的調(diào)用順序?yàn)椋孩侔凑赵谂缮惗x時(shí)的先后次序調(diào)用基類構(gòu)造函數(shù)。②按照在類定義中排列的先后順序依次調(diào)用成員對象的構(gòu)造函數(shù)。③執(zhí)行派生類構(gòu)造函數(shù)中的操作。派生類對象在撤消時(shí)是按照構(gòu)造函數(shù)調(diào)用相反的次序調(diào)用類的析構(gòu)函數(shù)。首先調(diào)用派生類析構(gòu)函數(shù),清除派生類中新增的數(shù)據(jù)成員;其次調(diào)用成員對象析構(gòu)函數(shù),清除派生類對象中的成員對象;最后調(diào)用基類的析構(gòu)函數(shù),清除從基類繼承來的數(shù)據(jù)成員。6.3派生類的構(gòu)造與析構(gòu)【例6-5】設(shè)計(jì)Teacher類,它繼承于Person類并組合了Date類。演示派生類對象在構(gòu)造和析構(gòu)時(shí),基類和成員對象構(gòu)造與析構(gòu)函數(shù)的調(diào)用情況。程序說明:從程序運(yùn)行結(jié)果可見,雖然在Teacher派生類構(gòu)造函數(shù)定義中成員對象(dateOfWork)聲明在基類(Person)前,但依然是基類構(gòu)造函數(shù)先被執(zhí)行。此外,運(yùn)行結(jié)果顯示析構(gòu)函數(shù)的調(diào)用次序正好與構(gòu)造函數(shù)調(diào)用順序相反。派生類構(gòu)造Teacher(char*n,bools,inty,intm,intd,inten,char*pt)的總參數(shù)表中參數(shù)的包括基類構(gòu)造函數(shù)Person(char*n,bools)和成員對象構(gòu)造函數(shù)Date(inty,intm,intd)的參數(shù)。在派生類構(gòu)造函數(shù)定義中,基類Person的聲明格式是Person(n,s),用Date類定義的成員對象dateOfWork的聲明格式是dateOfWork(y,m,d)。6.3派生類的構(gòu)造與析構(gòu)

C++支持從兩個及以上基類共同派生出新的派生類,這種繼承結(jié)構(gòu)被稱為多重繼承(MultipleInheritance)或多繼承。多重繼承能方便地描述事物的多種特征,能方便地支持代碼復(fù)用,具有結(jié)構(gòu)簡單清晰的優(yōu)點(diǎn)。6.4多重繼承與虛基類

6.4多重繼承與虛基類6.4.1多重繼承

多重繼承的派生類繼承于多個基類,在派生類定義時(shí),多個基類之間用逗號分隔。派生類對象初始化時(shí),將首先調(diào)用基類的構(gòu)造函數(shù),其調(diào)用順序是參照定義中的次序,如:classC:publicA,publicB{public: C():B(),A(){cout<<"CallCConstructor!"<<endl;}};

基類A的構(gòu)造函數(shù)先調(diào)用,盡管在構(gòu)造函數(shù)聲明中基類B的構(gòu)造函數(shù)在基類A的前面。

6.4多重繼承與虛基類由于多重繼承的基類不止一個,而不同的類其數(shù)據(jù)成員和成員函數(shù)有可能同名,此時(shí)派生類繼承了不同基類的同名成員,會出現(xiàn)無法訪問的二義性問題?!纠?-6】手機(jī)類和MP4播放器類為基類定義音樂手機(jī)類,并測試。

6.4多重繼承與虛基類程序說明:MobilePhone類和MusicPlayer類均擁有show成員函數(shù),在派生類中訪問show函數(shù)必須的指明是屬于哪個基類的成員函數(shù),方法是MobilePhone::show();,其中“::”是作用域標(biāo)識符。語句myObj.show();在編譯時(shí)出錯,所報(bào)錯誤為:對“show”的訪問不明確。如果將改語句為myObj.MusicPlayer::show();,則能通過編譯。6.4多重繼承與虛基類6.4.2虛基類

在例程6-6中,手機(jī)類和播放器類均含有商標(biāo)和價(jià)格數(shù)據(jù)成員,并且在派生類中包含了兩份同樣的數(shù)據(jù)。這種設(shè)計(jì)不僅浪費(fèi)存儲空間,而且會帶來數(shù)據(jù)更新的一致性問題。例如,若音樂手機(jī)降價(jià)了,則需要同時(shí)修改兩處price私有數(shù)據(jù)。一種比較自然的設(shè)計(jì)方法是定義一個商品類,其中包含商標(biāo)和價(jià)格數(shù)據(jù),而手機(jī)類和播放器類分別繼承于商品類。派生類的層次結(jié)構(gòu)如圖所示。6.4多重繼承與虛基類在多重繼承的類繼承層次結(jié)構(gòu)中,繼承于兩個不同基類的派生類,由于其基類又派生于同一個基類(不一定是直接基類),故可能出現(xiàn)如圖6-5所示的“鉆石繼承”(又稱菱形繼承)情況。此時(shí)商品類中的數(shù)據(jù)成員(如價(jià)格price)分別被其派生類手機(jī)類和播放器類所繼承,而音樂手機(jī)類又多重繼承于手機(jī)類和播放器類,因此,商品類的數(shù)據(jù)成員price分別通過手機(jī)類和播放器類派生給音樂手機(jī)類,同樣的數(shù)據(jù)成員在音樂手機(jī)派生類對象中將出現(xiàn)兩個,并且存儲地址也不相同。這樣不僅浪費(fèi)存儲空間,而且還會因?yàn)樾枰S護(hù)數(shù)據(jù)的一致性增加額外的開銷。下面的示例說明了多重繼承可能引發(fā)“鉆石繼承”問題?!纠?-7】多重繼承中存在的“鉆石繼承”和數(shù)據(jù)同步問題示例。6.4多重繼承與虛基類跟蹤與觀察:(1) 主函數(shù)中執(zhí)行了myObj.MusicPlayer::setPrice(500);,使得繼承于MusicPlayer的price數(shù)據(jù)的值為500,因而運(yùn)行結(jié)果顯示出1800和500兩個不同的價(jià)格。(2) 從圖6-6可知,基類繼承來的數(shù)據(jù)成員name和price在音樂手機(jī)派生類對象myObj中分別存儲了兩份,其中,從MobilePhone類繼承的name成員的存儲地址為0x002ff85c,price成員的存儲地址為0x002ff87c,而從MusicPlayer類繼承的name成員的存儲地址為0x002ff8a0,price成員的存儲地址為0x002ff8c0,并且兩個price成員的值不相同。6.4多重繼承與虛基類多重繼承中存在的鉆石繼承結(jié)構(gòu)將導(dǎo)致基類的數(shù)據(jù)成員在派生類對象中重復(fù)出現(xiàn)。為解決多重繼承在路徑匯聚點(diǎn)上的派生類因從不同路徑繼承了某個基類多次而產(chǎn)生重復(fù)繼承的問題,C++語言通過引入虛基類(VirtualBaseClass)來支持派生類對象在內(nèi)存中僅有基類數(shù)據(jù)成員的一份拷貝,以消除鉆石繼承所產(chǎn)生的數(shù)據(jù)重復(fù)存儲問題。虛基類定義的語法非常簡單,只需用virtual限定符在派生類定義時(shí)將基類的繼承方式聲明為虛擬的即可。例如:classMobilePhone:virtualpublicMerchandise{...}其中virtual和public關(guān)鍵字的次序可任意。6.4多重繼承與虛基類下面的程序是在例6-7的基礎(chǔ)上,通過定義虛基類解決派生類對象中基類數(shù)據(jù)成員重復(fù)存儲的問題?!纠?-8】用虛基類解決“鉆石繼承”問題。

6.4多重繼承與虛基類跟蹤與觀察:(1) 從圖可知,myObj對象中從不同路徑繼承的數(shù)據(jù)成員name和

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論