虛函數、虛繼承

前言:對於Virual關鍵字的使用情況之前一直是似懂非懂的。這次在複習過程中又一次遇到了有關派生類虛繼承之後的在內存中所佔的大小。其實之前看書的時候也曾幾次遇到過類似有關virtual的問題,每一次總感覺自己當下似是理解了,結果事後遇到類似的又再一次卡殼,可見還有很多知識是需要自己不斷去理解並掌握的。蹭着這次遇到的問題,在同學的幫助下,並結合網上早前就已有很多人對此做了很多探討。好記性不如爛筆頭,特此將這些獨到的見解收藏一番。

虛函數與虛繼承尋蹤 》(全文閱讀請點擊此處

此篇技術博文圖文並茂的爲我們分析了以下幾種情況:

  • 簡單類情況下的對象結構
  • 普通單重繼承後派生類的對象結構
  • 普通多重繼承後派生類的對象結構
  • 虛擬繼承下的多重繼承後的派生類的對象結構

此處,摘其小結:

  • 當類中包含虛函數時,則該類每個對象中在內存分配中除去數據外還包含了一個虛函數表指針(vfptr),指向虛函數表(vftable),虛函數表中存放了該類包含的虛函數的地址。
  • 當子類通過虛繼承的方式從父類中派生出來,此時稱父類爲子類的虛基類。子類中將包含虛基表指針(vbptr),指向虛基類表(vbtable)
  • 在單繼承形式下,子類將完全獲得父類的虛函數表和數據(假入父類中有虛函數的話)。如果子類中重寫了父類的虛函數,就會在虛函數表中原本記錄父類中虛函數的地址覆蓋爲子類中對應的重定義後的該函數地址,否則不做變動。如果在子類中定義了新的虛函數,則虛函數表中會追加一條記錄,記錄該函數的地址(虛函數表中是順序存放虛函數地址的,記住,雖然虛函數表中添加了內容,但是此時對於該類的大小來說並未發生改變,因爲始終只有一個指向虛函數表的指針vfptr)。
    注意:
    如果在派生類對象直接訪問自身中的重定義的虛函數是不會觸發多態機制的,因爲這個函數調用在編譯時期是可以確定的,編譯器只需要直接調用即可;
    當對父類指針賦予不同的子類指針時,在調用子類中重定義的虛函數時纔會觸發多態機制,即動態的在運行期間調用屬於子類的該函數。(可適度地瞭解下覆蓋override和重載overload的區別)
  • 在多重繼承形式下,派生類會把所有的父類按繼承時的順序包含在自身內部。每個父類對應一個單獨的虛函數表。多重繼承下,子類不再具有自身的虛函數表,它的虛函數表與第一個父類的虛函數表合併了。同樣的,如果子類重寫了任意父類的虛函數,都會覆蓋對應的函數地址記錄。如果MyClassC重寫了fun函數(兩個父類都有該函數),那麼兩個虛函數表的記錄都需要被覆蓋!

    此時由於多重繼承下將存在對公共基類的多份拷貝問題,爲了節省內存空間,故而出現了虛擬繼承,那麼將只生成一個共享的基類。

  • 虛繼承的引入把對象的模型變得複雜,除了每個基類和公共基類的虛函數表指針vfptr需要記錄外,每個虛擬繼承了的父類還需要記錄一個虛基類表vbtable的指針vbptr。

  • 虛基類表每項記錄了被繼承的虛基類子對象相對於虛基類表指針的偏移量。

    原文的舉例和作圖相呼應,十分出彩,極力推薦感興趣者通讀原文,此處我也只是用自己的方式做下筆記而已,不喜勿噴,謝謝~

    擴展:
    純虛函數是一種特殊的虛函數,在許多情況下,在基類中不能對虛函數給出有意義的實現,而把它聲明爲純虛函數,它的實現留給該基類的派生類去做。


    獻上另一篇文章可一起了解:C++虛函數表解析

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章