虛基類存在的意義
先來看一段代碼
#include<bits/stdc++.h>
using namespace std;
class A
{
public:
void fun()
{
cout << "A";
}
};
class B : public A
{
};
class C : public A
{
};
class D :public B, public C
{
};
int main(void)
{
D d;
d.fun();//這裏會報錯 原因:fun()不明確
}
爲什麼這裏會報錯呢 這是應爲A同時是B和C的基類,D同時繼承了B和C,此時要調用A的fun(),編譯器就會不明白到底是要調用B中的fun()還是C中的fun()。
如果將基類定義爲虛基類,則會使派生類中只保留一份拷貝。
#include<bits/stdc++.h>
using namespace std;
class A
{
public:
void fun()
{
cout << "A";
}
};
class B :virtual public A
{
};
class C :virtual public A
{
};
class D :public B, public C
{
};
int main(void)
{
D d;
d.fun();//輸出 A
}
爲什麼會這樣呢?
在定義了虛基類後,就等於告訴編譯器,這裏的基類A是B和C兩個派生類所共有的,對於調用B還是C都是對一個A而言。而對於第一個例子來說,在調用fun()的時候,相當於把A拷貝給了B和C,而彼此和A是無關聯的。
當然,不用虛基類也不是不行,爲了解決此問題,你可以這樣解決,顯示調用某類的函數
int main(void)
{
D d;
d.B::fun();//輸出A
}
單一虛繼承的內存結構
虛 擬繼承的子類的內存結構,和普通繼承完全不同。虛擬繼承的子類,有單獨的虛函數表, 另外也單獨保存一份父類的虛函數表,兩部分之間用一個四個字節的0x00000000來作爲分界。子類的內存中,首先是自己的虛函數表,然後是子類的數據 成員,然後是0x0,之後就是父類的虛函數表,之後是父類的數據成員。
如果子類沒有自己的虛函數,那麼子類就不會有虛函數表,但是子類數據和父類數據之間,還是需要0x0來間隔。
因此,在虛擬繼承中,子類和父類的數據,是完全間隔的,先存放子類自己的虛函數表和數據,中間以0x分界,最後保存父類的虛函數和數據。如果子類重載了父類的虛函數,那麼則將子類內存中父類虛函數表的相應函數替換。