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