1. 程式人生 > >C++物件記憶體模型2 (虛擬函式,虛指標,虛擬函式表)

C++物件記憶體模型2 (虛擬函式,虛指標,虛擬函式表)

C++物件記憶體模型2 (虛擬函式,虛指標,虛擬函式表)

從例子入手,考察如下帶有虛擬函式的類的物件記憶體模型:

class A {
public:
    virtual void vfunc1();
    virtual void vfunc2();
    void func1();
    void func2();
    virtual ~A();
private:
    int m_data1, m_data2;
}; 

class B : A {
public:
    virtual void vfunc1();;
    void func2();
    virtual ~B();
private:
    int m_data3;
};

class C : B {
public:
    virtual void vfunc1();
    void func();
private:
    int m_data1, m_data4;
};

注:在子類中出現與父類相同名稱的變數和非虛擬函式不是最佳實踐,這裡是為了說明其記憶體結構。

其物件記憶體結構見下圖。

                                               *圖片來源於侯捷老師

對其分析如下:

1. 每個含有虛擬函式的類在記憶體中多一根指標(vptr),見圖中a,b,c物件中第一個位置,儲存的是虛擬函式表(vtbl)所在的位置。

2. 虛擬函式表(vtbl)儲存著所有虛擬函式的位置,由於其動態繫結特性,在覆寫(override)後在子類中儲存的虛擬函式位置與父類中不相同。

3. 分析上述程式碼, B繼承A,所以A中的資料部分也被B繼承下來,同時B新增上了自己的資料部分m_data3,加之vptr,組成了B左側的記憶體佈局。

  A中的虛擬函式vfunc1(),vfunc2()可以被覆寫和動態繫結。

  所以在B中,vfunc1()被覆寫,其vtbl中對應項指向了新的函式的位置(亮藍色)。vfunc2()未被覆寫,仍然指向原先位置(深藍色)。

  C與B同理,vfunc1()被覆寫,其vtbl中對應項指向了新的函式的位置(橘黃色)。vfunc2()未被覆寫,仍然指向原先位置(深藍色)。

非虛擬函式靜態繫結,儲存在單獨的記憶體空間(code memory section,灰色函式部分),呼叫時把物件的this指標,傳給一個invisible引數,以便確定誰在呼叫函式。

4. 呼叫虛擬函式的語句的C語言形式如圖中下部分所示,其中n表示對應的函式在第幾個位置(編譯器在建立虛擬函式表的時候已知),從而實現動態繫結。