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; };
注:在子類中出現與父類相同名稱的變量和非虛函數不是最佳實踐,這裏是爲了說明其內存結構。ide
對其分析以下:函數
1. 每一個含有虛函數的類在內存中多一根指針(vptr),見圖中a,b,c對象中第一個位置,存儲的是虛函數表(vtbl)所在的位置。佈局
2. 虛函數表(vtbl)存儲着全部虛函數的位置(地址),因爲其動態綁定特性,在覆寫(override)後在子類中存儲的虛函數位置與父類中不相同。this
3. 分析上述代碼, B繼承A,因此A中的數據部分也被B繼承下來,同時B添加上了本身的數據部分m_data3,加之vptr,組成了B左側的內存佈局。spa
A中的虛函數vfunc1(),vfunc2()能夠被覆寫和動態綁定。指針
因此在B中,vfunc1()被覆寫,其vtbl中對應項指向了新的函數的位置(亮藍色)。vfunc2()未被覆寫,仍然指向原先位置(深藍色)。code
C與B同理,vfunc1()被覆寫,其vtbl中對應項指向了新的函數的位置(橘黃色)。vfunc2()未被覆寫,仍然指向原先位置(深藍色)。對象
非虛函數靜態綁定,存儲在單獨的內存空間(code memory section,灰色函數部分),調用時把對象的this指針,傳給一個invisible參數,以便肯定誰在調用函數。blog
4. 調用虛函數的語句的C語言形式如圖中下部分所示,其中n表示對應的函數在第幾個位置(編譯器在創建虛函數表的時候已知),從而實現動態綁定。繼承