C++中的继承与虚函数各种概念
 
?虛繼承與一般繼承
虛繼承和一般的繼承不同,一般的繼承,在目前大多數(shù)的C++編譯器實(shí)現(xiàn)的對(duì)象模型中,派生類對(duì)象會(huì)直接包含基類對(duì)象的字段。而虛繼承的情況,派生類對(duì)象不會(huì)直接包含基類對(duì)象的字段,而是通過(guò)一個(gè)間接的指針去存取基類對(duì)象中的字段。
?繼承的特性
?繼承是面向?qū)ο笾幸氲闹匾匦灾?#xff0c;它的一個(gè)重要的特點(diǎn)就是子類是父類,父類不是子類。也就是說(shuō):
??? 1. 如果基類指針指向子類對(duì)象(pbase=&pchild),則該指針只能調(diào)用基類定義了的函數(shù);(因?yàn)檫@個(gè)地方是靜態(tài)綁定,而靜態(tài)綁定所依賴的就是指針聲明時(shí)的類型)
??? 2. 如果子類指針指向基類對(duì)象(pchild=(child *)pbase),則會(huì)出問(wèn)題;(因?yàn)樽宇愔锌赡苡幸恍┓椒ㄊ腔愔袥](méi)有的,在編譯時(shí)靜態(tài)綁定一些子類中特有的方法可能導(dǎo)致運(yùn)行時(shí)沒(méi)有這個(gè)方法)
??? 3. 如果基類和子類定義了同名的函數(shù),則到底調(diào)用什么函數(shù),必須視該指針的原始類型而定,而不是視指針實(shí)際指向的對(duì)象類型而定。(這個(gè)地方其實(shí)就是一般繼承和虛函數(shù)繼承的區(qū)別:一般繼承靜態(tài)綁定,所以依據(jù)指針聲明的原始類型,而虛函數(shù)是進(jìn)行動(dòng)態(tài)綁定的,它是根據(jù)實(shí)際指針?biāo)赶虻膶?duì)象。)
??? 4. 就算子類繼承了父類的某個(gè)函數(shù)(而未改寫(xiě)它),該函數(shù)依然被視為父類的,該函數(shù)依然屬于父類的域中,該函數(shù)中使用的普通函數(shù)(非虛函數(shù))依然被視為父類的函數(shù)。
??? 5. 私有變量的繼承性:私有變量對(duì)子類是不可見(jiàn)的,即使是子類從父類繼承下來(lái)了,也仍然是不可見(jiàn)的,它僅僅能被父類的函數(shù)(沒(méi)有在子類中改寫(xiě)或重寫(xiě)過(guò)的函數(shù))操作。
??? 6. this指針:類的成員函數(shù)的參數(shù)中有一個(gè)隱藏的參數(shù)this指針,這保證了被繼承的成員函數(shù)歸屬對(duì)象的正確性和無(wú)混淆性。 
 
虛函數(shù)的實(shí)現(xiàn)原理
???? 正是因?yàn)槔^承的這個(gè)特點(diǎn),虛函數(shù)的加入似乎就是順理成章的事情了。虛函數(shù)簡(jiǎn)化并明確了軟件和各種類庫(kù)的設(shè)計(jì)以及維護(hù)。
??? 一般的函數(shù)在編譯鏈接時(shí)就進(jìn)行了綁定,這稱之為早綁定。由于信息量不夠,所以只能依賴于調(diào)用它的對(duì)象或指針的聲明類型實(shí)現(xiàn)綁定。也就是侯俊杰說(shuō)的,如果基類和子類定義了同名的函數(shù),那么到底調(diào)用哪個(gè)類的函數(shù),必須視該指針的原始類型而定,而不是視指針實(shí)際指向的對(duì)象類型而定。因?yàn)橘x值的動(dòng)作還沒(méi)有產(chǎn)生。
??? 而虛函數(shù)則不是這樣。虛函數(shù)實(shí)現(xiàn)的機(jī)制是晚綁定,它在編譯鏈接時(shí)并沒(méi)有與某個(gè)對(duì)象綁定----這也正是虛函數(shù)能實(shí)現(xiàn)多態(tài)(以相同代碼調(diào)用不同函數(shù))的原因所在。當(dāng)編譯器對(duì)程序進(jìn)行編譯碰到虛函數(shù)時(shí),將不會(huì)賦予一個(gè)地址,而是插入一段匯編代碼。每個(gè)包含虛函數(shù)的類都會(huì)由編譯器產(chǎn)生一個(gè)虛函數(shù)表和一個(gè)虛函數(shù)表指針,其中虛函數(shù)表指針?lè)旁诿總€(gè)類的首地址處(也許不是,不過(guò)反正地址偏移量在每個(gè)類所占內(nèi)存中是固定的,這個(gè)在其他文章中有專門詳述虛函數(shù)表)。當(dāng)程序執(zhí)行時(shí),碰到對(duì)虛函數(shù)的調(diào)用,則通過(guò)插入的匯編代碼到當(dāng)前類的地址中找到虛函數(shù)表指針,通過(guò)虛函數(shù)的序號(hào)找到需要調(diào)用的虛函數(shù)。注意,一個(gè)系列的類的虛函數(shù)表中某一個(gè)函數(shù)的序號(hào)是一樣的。而且,編譯器會(huì)保證在使用父類指針操作子類對(duì)象時(shí)只能在父類已有的虛函數(shù)上實(shí)現(xiàn)虛函數(shù)的機(jī)制。
??? 這里還有一個(gè)虛函數(shù)的默認(rèn)參數(shù)的問(wèn)題。虛函數(shù)是動(dòng)態(tài)綁定的,而默認(rèn)參數(shù)則是靜態(tài)綁定的,所以在虛函數(shù)中使用默認(rèn)參數(shù)可以說(shuō)是不符合邏輯的。如果子類改寫(xiě)了父類虛函數(shù)中的默認(rèn)參數(shù),當(dāng)使用多態(tài)特性時(shí),會(huì)出現(xiàn)調(diào)用子類的虛函數(shù),使用的卻是父類中的對(duì)應(yīng)虛函數(shù)的默認(rèn)參數(shù)的情況。 
 
虛函數(shù)適用的兩種場(chǎng)合
???? 1. 某個(gè)子類中調(diào)用繼承下來(lái)的非虛函數(shù)中有對(duì)已改寫(xiě)的虛函數(shù)的調(diào)用。
??? 值得注意的是,在某個(gè)子類中調(diào)用繼承下來(lái)的未改寫(xiě)的非虛函數(shù)中有對(duì)已改寫(xiě)的虛函數(shù)的調(diào)用時(shí),調(diào)用的是當(dāng)前子類中改寫(xiě)過(guò)的虛函數(shù);若該非虛函數(shù)中有對(duì)已改寫(xiě)的非虛函數(shù)的調(diào)用時(shí),調(diào)用的是父類的非虛函數(shù)(也是因?yàn)橥斫壎?#xff09;。這是MFC的慣用手法。
??? 2.使用向上映射(父類指針=子類指針),實(shí)現(xiàn)代碼的重用
??? 3.父類中的析構(gòu)函數(shù)。當(dāng)一個(gè)類確信不會(huì)成為任何類的父類時(shí),它的析構(gòu)函數(shù)是不需要設(shè)置成虛函數(shù)的;當(dāng)一個(gè)類肯定會(huì)成為某個(gè)類的父類時(shí),虛析構(gòu)函數(shù)是必要的。因?yàn)槿羰歉割愔械奈鰳?gòu)函數(shù)是非虛的,則當(dāng)用一個(gè)父類的指向子類的指針delete子類時(shí),這種行為在C++標(biāo)準(zhǔn)中并沒(méi)有被定義,是十分危險(xiǎn)的。 
 
繼承中的接口及其實(shí)現(xiàn)
?? 經(jīng)過(guò)以上分析可知,虛函數(shù)實(shí)際上就是繼承中的一種接口。繼承中一共有純虛函數(shù)、非純虛函數(shù)和非虛函數(shù)三種接口,它們?cè)谧宇愔械奶幚砣缦?#xff1a;
?? ?1.純虛函數(shù):所有子類必須強(qiáng)制性地改寫(xiě),否則會(huì)報(bào)錯(cuò)。這是一種僅僅繼承接口的方法。
???2.非純虛函數(shù):又被稱為簡(jiǎn)單虛函數(shù),可以在基類中有自己的實(shí)現(xiàn)(默認(rèn)的動(dòng)作),子類不一定要改寫(xiě),這是一種繼承接口及其默認(rèn)實(shí)現(xiàn)的方法。
?? ?3.非虛函數(shù):子類最好不要改寫(xiě),這是一種強(qiáng)制性地繼承接口及其實(shí)現(xiàn)的方法,表示的是一種共性。
?當(dāng)在同一個(gè)類中存在同名但是參數(shù)不同的函數(shù),叫作overloading(重載);子類改寫(xiě)父類的虛函數(shù),叫做overriding(覆蓋);子類改寫(xiě)父類的非虛函數(shù),叫做redefining(重定義),這是不推薦的。? 
 
虛函數(shù)、純虛函數(shù)、虛基類、抽象類、虛函數(shù)繼承、虛繼承-------各種概念解釋
?虛函數(shù):
??? 虛函數(shù)是C++中用于實(shí)現(xiàn)多態(tài)(polymorphism)的機(jī)制。核心理念就是通過(guò)基類訪問(wèn)派生類定義的函數(shù)。是C++中多態(tài)性的一個(gè)重要體現(xiàn),利用基類指針訪問(wèn)派生類中的成員函數(shù),這種情況下使用虛函數(shù),這種情況下采用的是動(dòng)態(tài)綁定技術(shù)。
??? 虛函數(shù)必須是基類的非靜態(tài)成員函數(shù),其訪問(wèn)權(quán)限可以是protected或public,在基類的類定義中定義虛函數(shù)的一般形式:
????? virtual 函數(shù)返回值類型 虛函數(shù)名(形參表)
????????? { 函數(shù)體 }
動(dòng)態(tài)綁定:
??? 基類指針是調(diào)用派生類的中的成員函數(shù)還是調(diào)用基類中的成員函數(shù)要到程序運(yùn)行時(shí)確定。主要要看指針?biāo)赶虻膶?duì)象。
純虛函數(shù):
??? 純虛函數(shù)是在基類中聲明的虛函數(shù),它在基類中沒(méi)有定義,但要求任何派生類都要定義自己的實(shí)現(xiàn)方法。在基類中實(shí)現(xiàn)純虛函數(shù)的方法是在函數(shù)原型后加“=0”
????????????? virtual void funtion1()=0
虛基類、抽象類:
??? 包含純虛函數(shù)的類稱為抽象類。由于抽象類包含了沒(méi)有定義的純虛函數(shù),所以不能定義抽象類的對(duì)象。
虛函數(shù)繼承:
  虛函數(shù)繼承就是覆蓋。即基類中的虛函數(shù)被派生類中的同名函數(shù)所覆蓋。  是實(shí)現(xiàn)多態(tài)的方法。
 
虛繼承:
解決多重繼承中派生類成員函數(shù)調(diào)用模糊問(wèn)題。比如類A中有一個(gè)函數(shù)print(),類B繼承A,類C繼承A,類D繼承類B和類C,這個(gè)時(shí)候,類D中就有兩個(gè)print函數(shù),一個(gè)是從B繼承得到的,一個(gè)是從C繼承得到的,則類D的對(duì)象調(diào)用print函數(shù)就會(huì)出現(xiàn)print模糊的編譯錯(cuò)誤。解決辦法:類B虛擬繼承A。類C虛擬繼承A,類D繼承B,C時(shí),只拷貝A中的數(shù)據(jù)成員和函數(shù)成員一次,再遇到拷貝時(shí)候就忽略了!
虛繼承就是為了節(jié)約內(nèi)存的,他是多重繼承中的特有的概念。適用與菱形繼承形式。
如:類B、C都繼承類A,D繼承類B和C。為了節(jié)省內(nèi)存空間,可以將B、C對(duì)A的繼承定義為虛擬繼承,此時(shí)A就成了虛擬基類。
class A;
class B:public vitual A;
class C:public vitual A;
class D:public B,public C;
參考:http://www.haogongju.net/art/1069038 
 
?http://www.diybl.com/course/3_program/c++/cppsl/2008520/117246.html
?一個(gè)介紹虛函數(shù)的很好的ppt:pdf下載,ppt下載
總結(jié)
以上是生活随笔為你收集整理的C++中的继承与虚函数各种概念的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
 
                            
                        - 上一篇: OGR示例:写shp,求面与面的交和差操
- 下一篇: Win7下WinDbg开启Local K
