多態和抽象(二)
①多態的特點是,類中有虛函數。抽象的特點是,類中有純虛函數。
②多態的基類可以實例化對象,抽象類不可以實例化對象。如果一個抽象類的派生類沒有實現這個抽象類的純虛函數,那麽這個派生類也是個抽象類,也是不能實例化對象。
③析構函數不是虛函數會有什麽影響?在下面程序中,B是A的派生類,有一個A*類型的指針new了一個B類型的對象。當delete p的時候,如果A和B的析構函數是虛函數,那麽調用的就是B的析構函數,如果不是析構函數,那麽調用的就是A的析構函數。
class A { public: A(); virtual ~A(); } class B : public A { public: B(); virtual ~B(); } int main() { A* p = new B(); delete p; return 0; }
④虛函數表。這個比較難解釋,但是理解了虛函數表,就理解了多態的用處。轉自下面這個博客。
https://www.cnblogs.com/wangxiaobao/p/5850949.html
從例子入手,考察如下帶有虛函數的類的對象內存模型:
1 class A { 2 public: 3 virtual void vfunc1(); 4 virtual void vfunc2(); 5 void func1(); 6 void func2(); 7 virtual ~A(); 8 private: 9 int m_data1, m_data2; 10 }; 11 12 class B : A { 13 public: 14 virtual void vfunc1();; 15 void func2(); 16 virtual ~B(); 17 private: 18 int m_data3; 19 }; 20 21 class C : B { 22 public: 23 virtual void vfunc1(); 24 void func(); 25 private: 26 int m_data1, m_data4; 27 };
註:在子類中出現與父類相同名稱的變量和非虛函數不是最佳實踐,這裏是為了說明其內存結構。
其對象內存結構見下圖。
*圖片來源於侯捷老師
對其分析如下:
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表示對應的函數在第幾個位置(編譯器在建立虛函數表的時候已知),從而實現動態綁定。
多態和抽象(二)