1. 程式人生 > >多態和抽象(二)

多態和抽象(二)

就是 target 名稱 沒有 span toolbar 調用 數位 public

①多態的特點是,類中有虛函數。抽象的特點是,類中有純虛函數。

②多態的基類可以實例化對象,抽象類不可以實例化對象。如果一個抽象類的派生類沒有實現這個抽象類的純虛函數,那麽這個派生類也是個抽象類,也是不能實例化對象。

③析構函數不是虛函數會有什麽影響?在下面程序中,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表示對應的函數在第幾個位置(編譯器在建立虛函數表的時候已知),從而實現動態綁定。

多態和抽象(二)