簡述c++虛函式作用及底層實現原理c++是物件導向程式設計,其包括3項特點:
(1)資料抽象:介面和實現分離
(2)繼承:父類和子類
(3)多型:動態繫結
本文討論多型。
當父類希望子類重新定義某些函式時,用virtual關鍵字宣告為虛函式。
當我們使用乙個基類型別的引用或者指標,呼叫乙個虛函式時就引發動態繫結/多型的發生。函式執行版本由傳入的實參型別決定。可以用父型別的指標指向其子類的例項,然後通過父類的指標呼叫實際子類的成員函式。
父類指標好像有「多種型別」。這是一種泛型技術,試圖用不變的**實現可變的演算法,比如模板技術,虛函式技術等。
引用或者指標的靜態型別與動態型別不同這一事實,是c++支援多型的核心。上述這句話選自《c++ primer》,頗有玄門正宗之感。複雜的技術根植於底層的原理,所以搞清原理方可深入淺出。
當我們使用基類的引用、指標去呼叫基類中定義的乙個虛函式時,並不知道該函式真正作用的物件是什麼型別,可能是乙個基類物件,也可能是乙個派生類物件,這一切直到執行時才可知道。所以多型又稱執行時繫結/動態繫結。
對於非虛函式的呼叫在編譯時即可確定,哪個物件,哪個方法,位址是確定的。
當你記住上面的概念時,它僅是概念。但當真正了解底層原理後,發現其中妙不可言。
如果讓你來實現虛函式的功能?你會怎麼實現?
複雜的**?高深的原理?其實c++中實現的非常接地氣。
虛函式是通過虛函式表和虛函式指標來實現的。
該錶一般位於某型別的物件例項在記憶體中的最開始的位置。
class base
virtual
void g()
virtual
void h()
};
父類物件其在記憶體中布局示意如下:
虛函式表的尾部為虛函式表的結束結點。
再定義乙個子類,此時並不覆蓋父類的虛函式:
此時可以得知:
(1)虛函式按照宣告順序放在表中;
(2)父類的虛函式,排在子類虛函式之前。
當我們把子類中的函式覆蓋時:
此時可以得知:
(1)子類覆蓋的虛函式,放在原來父類該虛函式的位置;所以當父類指標指向該子類物件時,呼叫方法就會呼叫子類過載的方法;
(2)沒有被覆蓋的虛函式依舊。
當發生多類繼承時:
虛函式表記憶體排列示意如下:
此時可以得知:
(1)每個繼承的父類,都有自己的虛函式表;
(2)子類虛函式,放在第乙個宣告順序的父類的表中。
當子類虛函式重寫時,此時類的繼承為:
子類將f( )函式重寫,則記憶體中的排列為:
此時我們可以得知:
(1)三個父類虛函式表中的f( )函式被替換為子類函式指標。因此當我們用任一靜態型別的父類指標來指向子類時,呼叫f( )時就呼叫的子類的f( )。
參考資料:
[1]
C 中虛繼承的作用及底層實現原理
虛繼承和虛函式是完全無相關的兩個概念。虛繼承是解決c 多重繼承問題的一種手段,從不同途徑繼承來的同一基類,會在子類中存在多份拷貝。這將存在兩個問題 其一,浪費儲存空間 第二,存在二義性問題,通常可以將派生類物件的位址賦值給基類物件,實現的具體方式是,將基類指標指向繼承類 繼承類有基類的拷貝 中的基類...
C 中虛繼承的作用及底層實現原理
虛繼承和虛函式是完全無相關的兩個概念。虛繼承是解決c 多重繼承問題的一種手段,從不同途徑繼承來的同一基類,會在子類中存在多份拷貝。這將存在兩個問題 其一,浪費儲存空間 第二,存在二義性問題,通常可以將派生類物件的位址賦值給基類物件,實現的具體方式是,將基類指標指向繼承類 繼承類有基類的拷貝 中的基類...
C 虛函式的底層實現原理
在c 中,多型是利用虛函式來實現的。比如說,有如下 include using namespace std class animal class dog public animal void makeanimalcry animal animal int main 輸出如下圖 這裡定義了乙個anim...