C++ 鏈式繼承下的虛函數列表

目錄

1.虛函數列表的位置

2.虛函數列表的內容

3.鏈式繼承中虛函數列表的內容

 

注:ios

虛函數列表 又稱爲虛表, vtbl , 指向它的指針稱爲vptr, vs2019中稱爲__vfptrwindows

操做系統: windows 10 專業版 64位markdown

編譯器: Visual Studio 2019 Communityide

 

1.虛函數列表的位置

結論

編譯器通常會保證指向虛函數列表的指針存在於對象實例中最前面的位置函數

而虛函數列表中的內容, 就是多個函數指針佈局

代碼驗證:

首先聲明一個基類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對象的地址, 和控制檯打印的內容是相同的

 

2.單繼承中虛函數列表的內容

基類中有4個函數, 分別爲

f(); 
g(); 
h(); 
i();

派生類中有2個函數,分別爲

g();
i1();

如今使用表格的方式表示出來, 方便查看, 進行了override的函數, 會放在同一行

結論 在虛函數列表中, 函數的佈局以下圖所示:

代碼驗證請看鏈式繼承中虛函數列表的內容

 

3.鏈式繼承中虛函數列表的內容

聲明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; }
};

用表格的方法表示爲:

!()(https://silenzio-markdown-image-hosting-service.oss-cn-beijing.aliyuncs.com/%E5%8D%9A%E5%AE%A2%E5%9B%BE%E5%BA%8A/C%2B%2B%20%E9%93%BE%E5%BC%%E7%BB%A7%E6%89%BF%E4%B8%8B%E7%9A%84%E8%99%9A%E5%87%BD%E6%95%B0%E5%88%97%E8%A1%A8/1a885406e3318ccbbf5dce9961e1599.png)

結論

在虛函數列表中, 函數的佈局以下圖所示:

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;
}
相關文章
相關標籤/搜索