注:ios
虛函數列表 又稱爲虛表, vtbl , 指向它的指針稱爲vptr, vs2019中稱爲__vfptrwindows
操做系統: windows 10 專業版 64位markdown
編譯器: Visual Studio 2019 Communityide
編譯器通常會保證指向虛函數列表的指針存在於對象實例中最前面的位置函數
而虛函數列表中的內容, 就是多個函數指針佈局
首先聲明一個基類Base
和一個派生類Derived
操作系統
class Base { public: virtual void f() { std::cout << "Base1::f" << std::endl; } virtual void g() { std::cout << "Base1::g" << std::endl; } virtual void h() { std::cout << "Base1::h" << std::endl; } virtual void i() { std::cout << "Base1::i" << std::endl; } }; class Derived : public Base { virtual void g() override { std::cout << "Derived::g" << std::endl; } virtual void h1() { std::cout << "Derived::i1" << std::endl; } };
而後實例化一個派生類的對象3d
Derived derived;
如今咱們打印出該對象的地址指針
std::cout << "derived對象的地址: " << (&derived) << std::endl;
因爲咱們假定指向虛函數列表的<指針>存在於對象實例中最前面的位置
code
那麼咱們能夠認定, derived對象的地址
中的開頭是一個指針
的地址(稱之爲指針pA)
而這個指針(pA)指向虛函數列表中的開頭, 也就是一個函數指針(稱之爲指針pF)
因此這個指針(pA), 是一個指向指針的指針
, 即指向指針(pF)的指針(pA)
基於這個推測, 咱們將derived對象的地址
即指針pA的地址
進行一個類型轉換
使用reinterpret_cast<int**>
關鍵字, 將其轉換爲一個指向指針的指針
reinterpret_cast<int**>(&derived)
如今咱們對這個指針(pA)的地址, 取其內容, 就會獲得pA的內容
std::cout << "derived對象中第一個指針的內容: " << *reinterpret_cast<int**>(&derived) << std::endl;
根據上面的推測, 這個內容, 就是虛函數列表的地址
控制檯輸出以下:
經過vs2019中, 能夠直接查看到derived
的__vfptr對象的地址, 和控制檯打印的內容是相同的
基類中有4個函數, 分別爲
f(); g(); h(); i();
派生類中有2個函數,分別爲
g(); i1();
如今使用表格的方式表示出來, 方便查看, 進行了override的函數, 會放在同一行
代碼驗證請看鏈式繼承中虛函數列表的內容
聲明3個類, 其繼承關係爲Derived
繼承Base2
, Base2
繼承Base1
class Base1 { public: virtual void f() { std::cout << "Base1::f" << std::endl; } virtual void g() { std::cout << "Base1::g" << std::endl; } virtual void h() { std::cout << "Base1::h" << std::endl; } virtual void i() { std::cout << "Base1::i" << std::endl; } }; class Base2 : public Base1{ public: virtual void f()override { std::cout << "Base2::f" << std::endl; } virtual void h1() { std::cout << "Base2::h1" << std::endl; } }; class Derived : public Base2 { public: virtual void g()override { std::cout << "Derived::g" << std::endl; } virtual void i1() { std::cout << "Derived::i1" << std::endl; } };
用表格的方法表示爲:
在虛函數列表中, 函數的佈局以下圖所示:
Derive只有一個虛函數表, 是在Base2的虛函數表上, 進行相似於單繼承的覆蓋 同理, Base2也有一張虛函數表, 是在Base1的虛函數表上, 進行單繼承的覆蓋
//////////////////////////////////////////////////////////////////////////////// // 鏈式繼承 //////////////////////////////////////////////////////////////////////////////// #include <iostream> class Base1 { public: virtual void f() { std::cout << "Base1::f" << std::endl; } virtual void g() { std::cout << "Base1::g" << std::endl; } virtual void h() { std::cout << "Base1::h" << std::endl; } virtual void i() { std::cout << "Base1::i" << std::endl; } }; class Base2 : public Base1{ public: virtual void f()override { std::cout << "Base2::f" << std::endl; } virtual void h1() { std::cout << "Base2::h1" << std::endl; } }; class Derived : public Base2 { public: virtual void g()override { std::cout << "Derived::g" << std::endl; } virtual void i1() { std::cout << "Derived::i1" << std::endl; } }; using Fun = void(*)(void); int main() { Fun pFun = nullptr; // 操做系統: windows 10 專業版 32/64位均可以 // 編譯器 : Visual Studio 2019 Community std::cout << sizeof(int) << std::endl; // 32位:4 64位:4 std::cout << sizeof(long) << std::endl; // 32位:4 64位:4 std::cout << sizeof(int*) << std::endl; // 32位:4 64位:8 std::cout << "-------------------------------------------------------------------------------------- " << std::endl; std::cout << "Base1的虛表以下 " << std::endl; Base1 base1; std::cout << "base1對象的地址: " << (&base1) << std::endl; std::cout << "base1對象中第一個指針的地址(不是內容): " << (&base1) << std::endl; // &base1 是一個指向指針的指針 // 對它進行*操做, 獲得了這個指針的內容, 即它指向的位置, 是一個虛表(虛表的內容也是一堆指針) std::cout << "base1對象中第一個指針的內容: " << *reinterpret_cast<int**>(&base1) << std::endl; std::cout << "base1虛函數表地址: " << *reinterpret_cast<int**>(&base1) << std::endl; // 虛函數表地址, 也是一個指向指針的指針 // 對它進行*操做, 獲得了這個指針的內容, 即它指向的位置, 是一個函數指針 std::cout << "base1虛函數表 — 第一個函數地址:" << (*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&base1))))) << std::endl; pFun = reinterpret_cast<Fun>(* (reinterpret_cast<int**>(*(reinterpret_cast<int**>(&base1))))); std::cout << "base1虛函數表 — 第一個函數內容:"; pFun(); // base1::f std::cout << std::endl; // 注意次數的偏移量, 32位偏移量是+1, 64位偏移量是+2 std::cout << "base1虛函數表 — 第二個函數地址:" << (*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&base1))+1*(sizeof(int*)/sizeof(int))))) << std::endl; pFun = reinterpret_cast<Fun>(*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&base1)) + 1 * (sizeof(int*) / sizeof(int))))); std::cout << "base1虛函數表 — 第二個函數內容:"; pFun(); // base1::g std::cout << std::endl; std::cout << "base1虛函數表 — 第三個函數地址:" << (*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&base1)) + 2 * (sizeof(int*) / sizeof(int))))) << std::endl; pFun = reinterpret_cast<Fun>(*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&base1)) + 2 * (sizeof(int*) / sizeof(int))))); std::cout << "base1虛函數表 — 第三個函數內容:"; pFun(); // base1::h std::cout << std::endl; std::cout << "base1虛函數表 — 第四個函數地址:" << (*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&base1)) + 3 * (sizeof(int*) / sizeof(int))))) << std::endl; pFun = reinterpret_cast<Fun>(*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&base1)) + 3 * (sizeof(int*) / sizeof(int))))); std::cout << "base1虛函數表 — 第四個函數內容:"; pFun(); // base1::i std::cout << std::endl; std::cout << "-------------------------------------------------------------------------------------- " << std::endl; std::cout << "Base2的虛表以下 " << std::endl; Base2 base2; std::cout << "base2對象的地址: " << (&base2) << std::endl; std::cout << "base2對象中第一個指針的地址(不是內容): " << (&base2) << std::endl; // &base1 是一個指向指針的指針 // 對它進行*操做, 獲得了這個指針的內容, 即它指向的位置, 是一個虛表(虛表的內容也是一堆指針) std::cout << "base2對象中第一個指針的內容: " << *reinterpret_cast<int**>(&base2) << std::endl; std::cout << "base2虛函數表地址: " << *reinterpret_cast<int**>(&base2) << std::endl; // 虛函數表地址, 也是一個指向指針的指針 // 對它進行*操做, 獲得了這個指針的內容, 即它指向的位置, 是一個函數指針 std::cout << "base2虛函數表 — 第一個函數地址:" << (*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&base2))))) << std::endl; pFun = reinterpret_cast<Fun>(*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&base2))))); std::cout << "base2虛函數表 — 第一個函數內容:"; pFun(); // base2::f std::cout << std::endl; std::cout << "base2虛函數表 — 第二個函數地址:" << (*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&base2)) + 1 * (sizeof(int*) / sizeof(int))))) << std::endl; pFun = reinterpret_cast<Fun>(*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&base2)) + 1 * (sizeof(int*) / sizeof(int))))); std::cout << "base2虛函數表 — 第二個函數內容:"; pFun(); // base1::g std::cout << std::endl; std::cout << "base2虛函數表 — 第三個函數地址:" << (*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&base2)) + 2 * (sizeof(int*) / sizeof(int))))) << std::endl; pFun = reinterpret_cast<Fun>(*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&base2)) + 2 * (sizeof(int*) / sizeof(int))))); std::cout << "base2虛函數表 — 第三個函數內容:"; pFun(); // base1::h std::cout << std::endl; std::cout << "base2虛函數表 — 第四個函數地址:" << (*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&base2)) + 3 * (sizeof(int*) / sizeof(int))))) << std::endl; pFun = reinterpret_cast<Fun>(*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&base2)) + 3 * (sizeof(int*) / sizeof(int))))); std::cout << "base2虛函數表 — 第四個函數內容:"; pFun(); // base1::i std::cout << std::endl; std::cout << "base2虛函數表 — 第五個函數地址:" << (*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&base2)) + 4 * (sizeof(int*) / sizeof(int))))) << std::endl; pFun = reinterpret_cast<Fun>(*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&base2)) + 4 * (sizeof(int*) / sizeof(int))))); std::cout << "base2虛函數表 — 第五個函數內容:"; pFun(); // base2::h1 std::cout << std::endl; std::cout << "-------------------------------------------------------------------------------------- " << std::endl; std::cout << "Derived的虛表以下 " << std::endl; Derived Derived; std::cout << "Derived對象的地址: " << (&Derived) << std::endl; std::cout << "Derived對象中第一個指針的地址(不是內容): " << (&Derived) << std::endl; // &base1 是一個指向指針的指針 // 對它進行*操做, 獲得了這個指針的內容, 即它指向的位置, 是一個虛表(虛表的內容也是一堆指針) std::cout << "Derived對象中第一個指針的內容: " << *reinterpret_cast<int**>(&Derived) << std::endl; std::cout << "Derived虛函數表地址: " << *reinterpret_cast<int**>(&Derived) << std::endl; // 虛函數表地址, 也是一個指向指針的指針 // 對它進行*操做, 獲得了這個指針的內容, 即它指向的位置, 是一個函數指針 std::cout << "Derived虛函數表 — 第一個函數地址:" << (*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&Derived))))) << std::endl; pFun = reinterpret_cast<Fun>(*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&Derived))))); std::cout << "Derived虛函數表 — 第一個函數內容:"; pFun(); // base2::f std::cout << std::endl; std::cout << "Derived虛函數表 — 第二個函數地址:" << (*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&Derived)) + 1 * (sizeof(int*) / sizeof(int))))) << std::endl; pFun = reinterpret_cast<Fun>(*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&Derived)) + 1 * (sizeof(int*) / sizeof(int))))); std::cout << "Derived虛函數表 — 第二個函數內容:"; pFun(); // Derived::g std::cout << std::endl; std::cout << "Derived虛函數表 — 第三個函數地址:" << (*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&Derived)) + 2 * (sizeof(int*) / sizeof(int))))) << std::endl; pFun = reinterpret_cast<Fun>(*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&Derived)) + 2 * (sizeof(int*) / sizeof(int))))); std::cout << "Derived虛函數表 — 第三個函數內容:"; pFun(); // base1::h std::cout << std::endl; std::cout << "Derived虛函數表 — 第四個函數地址:" << (*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&Derived)) + 3 * (sizeof(int*) / sizeof(int))))) << std::endl; pFun = reinterpret_cast<Fun>(*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&Derived)) + 3 * (sizeof(int*) / sizeof(int))))); std::cout << "Derived虛函數表 — 第四個函數內容:"; pFun(); // base1::i std::cout << std::endl; std::cout << "Derived虛函數表 — 第五個函數地址:" << (*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&Derived)) + 4 * (sizeof(int*) / sizeof(int))))) << std::endl; pFun = reinterpret_cast<Fun>(*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&Derived)) + 4 * (sizeof(int*) / sizeof(int))))); std::cout << "Derived虛函數表 — 第五個函數內容:"; pFun(); // base2::h1 std::cout << std::endl; std::cout << "Derived虛函數表 — 第六個函數地址:" << (*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&Derived)) + 5 * (sizeof(int*) / sizeof(int))))) << std::endl; pFun = reinterpret_cast<Fun>(*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&Derived)) + 5 * (sizeof(int*) / sizeof(int))))); std::cout << "Derived虛函數表 — 第六個函數內容:"; pFun(); // Derived::i1 std::cout << std::endl; return 0; }