探索c++的對象模型(二):菱形繼承的多態模型

首先我們已經知道了多繼承和單繼承的對象模型
接下來我們來看一個複雜的菱形繼承

菱形繼承的多態繼承
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<iostream>
using namespace std;
class A
{
public:
      virtual void func1()
     {
          printf("A::func1");
     }
public:
     int _a;
};
class B :public A
{
public:
     virtual void func1()
     {
           printf("B::func1");
     }
public:
     int _b;
};
class C :public A
{
public:
     virtual void func1()
     {
           printf("C::func1");
     }
public:
     int _c;
};
class D : public B, public C
{
public:
     virtual void func1()
     {
           printf("D::func1");
     }
public:
     int _d;
};
int main()
{
     D d;
     //B
     d.B::_a = 2;
     d._b = 3;
     //C
     d.C::_a = 4;
     d._c = 3;
     //D
     d._d = 4;
     printf("%d", sizeof(d));
     system("pause");
     return 0;
}


然偶我們再將虛表打出來看一看這兩個虛表指針的裏保存的虛函數各是什麼




我們可以明顯的看到,B裏的虛表和C裏的虛表此時都被D繼承了之後進行了虛函數的重寫,因爲D繼承了B和C所以都被D重寫了虛函數此時就打出來的是D的函數

菱形虛繼承的多態模型
class A
{
public:
     virtual void func1()
     {
           printf("A::func1\n");
     }
public:
     int _a;
};
class B :virtual public A
{
public:
     virtual void func1()
     {
           printf("B::func1\n");
     }
public:
     int _b;
};
class C :virtual public A
{
public:
     virtual void func1()
     {
           printf("C::func1\n");
     }
public:
     int _c;
};
class D : public B, public C
{
public:
     virtual void func1()
     {
           printf("D::func1\n");
     }
public:
     int _d;
};

int main()
{
     D d;
     //B
     d.B::_a = 2;
     d._b = 3;
     //C
     d.C::_a = 4;
     d._c = 3;
     //D
     d._d = 4;
     //D的虛表
      PrintVTable((int *)*((int *)&d + (sizeof(D)- sizeof(A))/4));
     system("pause");
     return 0;
}

typedef void(*FUNC) ();
void PrintVTable(int *VTable)
{
     printf("虛表地址:%p\n", VTable);
     int i = 0;
     for (; VTable[i] != NULL; i++)
     {
           printf("第%d個虛函數的地址:%p------>", i, VTable[i]);
           FUNC f = (FUNC)VTable[i];
           f();
     }
     printf("\n");
}

我們可以看到在D繼承了B和C之後,首先B和C繼承了A的虛表,同時對A進行了虛函數的重寫,但是當有兩個虛函數同時對A的虛函數進行重寫的時候,此時我們其實是不知道A到底是被B還是C重寫的,所以當D繼承了B和C之後也對這個虛函數進行了重寫,最後其實最後D對A的虛函數進行了重寫

打印虛表


模型


我們再來看一種情況
當子類裏不只有重寫的虛函數,還有他自己的沒有被重寫的虛函數這會是怎樣的呢?
1.我們先在B里加一個虛函數
class B :virtual public A
{
public:
     virtual void func1()
     {
           printf("B::func1\n");
     }
     virtual void func2()
     {
           printf("B::func2\n");
     }
public:
     int _b;
};

然後我們再來看內存結構,我們會發現D多了4個字節,通過看內存可以發現,我們的B結構裏多了一個地址,我們能確定的是這兩個地址裏肯定有一個是指向虛基表,那麼另一個其實就是虛表啦
我們在徐繼承的時候其實B和C都會繼承A的虛表它不會自己去在浪費內存再開一個虛表,但是此前提是他們兩個都沒有多餘的虛函數,一旦當他們兩個任意一個有了多餘的虛函數,那麼此時他們自己就會在生成一個虛表,將自己的虛函數放進去,誰有誰開,兩個都有的話兩個都開






然後在兩個同時加虛函數
class B :virtual public A
{
public:
     virtual void func1()
     {
           printf("B::func1\n");
     }
     virtual void func2()
     {
           printf("B::func2\n");
     }
public:
     int _b;
};
class C :virtual public A
{
public:
     virtual void func1()
     {
           printf("C::func1\n");
     }
     virtual void func3()
     {
           printf("C::func3\n");
     }
public:
     int _c;
};





虛基表
接下來我們再來看一下虛基表,我們之前發現虛基表裏其實存的是一個偏移量,那我們在把內存裏的虛基表看一下
其實這兩種虛表是編譯器中最常見的兩種虛表,第一行是判斷此類有無虛表,第二行很明顯是虛繼承偏移量


發佈了55 篇原創文章 · 獲贊 17 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章