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