虛函數表及虛函數表指針(看似簡單,深入部分也不是很懂)

回顧一下以前對虛函數表及虛表指針的概念:

1. 虛函數表屬於類,同類對象間共享該虛函數表(貌似虛函數表裏面維護了一個函數地址的指針數組)。

2.不同對象各自維護一個虛表指針指向類的虛表,類對象大小包含成員變量大小(含虛表指針vptr大小) ——(計算大小要考慮字節對齊 32位默認4字節對齊,64位8字節)。

3.虛表指針在對象內存的開始位置。

4.虛函數調用時,動態聯編的多態特性及運行時判斷執行哪個函數,所以虛函數執行效率比普通函數要低。

5.子類繼承父類後,如果子類有父類的重名虛函數,則會覆蓋(重寫)父類的虛函數,虛表內函數指針也會替換成子類的。

 

 

class Base{
  virtual void print(){}
};

class Base2{
  virtual void dprint(){}
};

class CBase: public Base, public Base2{

};

//g++  align.cpp -fdump-class-hierarchy

-------------------------------------linux 64位系統------------------------------------------------

Vtable for Base    //虛函數表內存儲了虛函數的地址
Base::_ZTV4Base: 3u entries    //虛表是屬於類,而不是屬於某個具體的對象,一個類只需要一個虛表
0     (int (*)(...))0            //一個空的變長參數函數指針??
8     (int (*)(...))(& _ZTI4Base)  //一個指向虛函數表的指針 = 把虛函數表地址轉成變長參數的函數指針?
16    Base::print

//24 xxx

Class Base
   size=8 align=8
   base size=8 base align=8
Base (0x7f1f07d2bd90) 0 nearly-empty
    vptr=((& Base::_ZTV4Base) + 16u) //可以看出虛函數表首地址還有兩個函數指針,偏移16字節後是第一個虛函數的地址

//虛函數表指針vptr指向的是第一個虛函數,而不是虛函數表首地址

//同理,32位系統指針4字節,則應該是虛函數表指針=虛函數表首地址 + 8字節偏移 = 第一個虛函數地址

Vtable for Base2
Base2::_ZTV5Base2: 3u entries
0     (int (*)(...))0
8     (int (*)(...))(& _ZTI5Base2)
16    Base2::dprint

Class Base2
   size=8 align=8
   base size=8 base align=8
Base2 (0x7f1f07d2bf50) 0 nearly-empty        //每個類的vptr放在類內存空間的起始位置
    vptr=((& Base2::_ZTV5Base2) + 16u)

Vtable for CBase
CBase::_ZTV5CBase: 6u entries
0     (int (*)(...))0
8     (int (*)(...))(& _ZTI5CBase)
16    Base::print
24    (int (*)(...))-0x00000000000000008
32    (int (*)(...))(& _ZTI5CBase)
40    Base2::dprint

Class CBase   //C++多重繼承中,會順序存儲所有基類的虛函數表指針(即有多個虛表指針)
   size=16 align=8   

   base size=16 base align=8
CBase (0x7f1f07d91380) 0
    vptr=((& CBase::_ZTV5CBase) + 16u)    //子類複用第一個基類的虛表指針,並對虛表進行覆蓋或者添加
  Base (0x7f1f07d96070) 0 nearly-empty
      primary-for CBase (0x7f1f07d91380)  //第一個基類會用於初始化子類(我認爲是初始化子類虛表),然後子類對象的虛表指針指向這個子類虛表。
  Base2 (0x7f1f07d960e0) 8 nearly-empty
      vptr=((& CBase::_ZTV5CBase) + 40u)  //子類對象第二個虛表指針,指向子類虛表中來自第二個基類的第一個虛函數地址

___________________________________________________________

class Base{
  virtual void print(){}
};

class CBase: public Base{

};
 

Vtable for Base
Base::_ZTV4Base: 3u entries
0     (int (*)(...))0
8     (int (*)(...))(& _ZTI4Base)
16    Base::print

Class Base
   size=8 align=8
   base size=8 base align=8
Base (0x7f1eeb8e9d90) 0 nearly-empty
    vptr=((& Base::_ZTV4Base) + 16u)

Vtable for CBase
CBase::_ZTV5CBase: 3u entries
0     (int (*)(...))0
8     (int (*)(...))(& _ZTI5CBase)
16    Base::print

Class CBase
   size=8 align=8
   base size=8 base align=8
CBase (0x7f1eeb954070) 0 nearly-empty
    vptr=((& CBase::_ZTV5CBase) + 16u)
  Base (0x7f1eeb9540e0) 0 nearly-empty
      primary-for CBase (0x7f1eeb954070)

Class Base2
   size=8 align=8
   base size=8 base align=8
Base2 (0x7f16e5f4ea10) 0 nearly-empty
    vptr=((& Base2::_ZTV5Base2) + 16u)

Vtable for CBase
CBase::_ZTV5CBase: 5u entries
0     (int (*)(...))0
8     (int (*)(...))(& _ZTI5CBase)
16    Base::print
24    Base::test
32    CBase::ok  //子類虛函數地址會放在基類後面

某大牛如是說:子類中聲明的虛函數除了覆蓋各個基類對應函數的指針外,還額外添加一份到第一個基類的vptr中(體現了共用的意義)。我表示對共用這個詞持保留意見,第一虛表屬於各個類,父類和子類不是同一個;第二虛表指針屬於對象,各個對象都有。子類複用繼承下來的父類聲明的虛表指針而已,該指針指向的是子類虛表。

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