1、菱形繼承
一個類由幾個類派生得到,而這幾類中有多個類又同時繼承同一個類。如下圖所示:Derive繼承A、B,A、B又同時繼承Base。

class Base {
public:
char ch;
};
class A: public Base {
public:
char a;
};
class B : public Base {
public:
char b;
};
class Derive : public A, public B {
public:
char d;
};
菱形繼承導緻的問題:菱形繼承會造成在執行個體化Derive類的時候,執行個體化得到的對象實際包含了兩份Base類的對象,造成資料備援和資料的二義性。通過下圖的記憶體分布,可以看到Derive類裡面包含了兩個ch成員,這兩個ch分别由A、B繼承得到,當通過Derive類對象通路ch時,就需要通過作用域運算符來表明通路的是A類繼承的ch還是B類繼承的ch(例:Derive d; d.A::ch;),否則會因為不明确而導緻出錯。這就導緻了資料的備援與二義性。
解決菱形繼承導緻的問題:通過虛繼承的方式來解決。
2、虛繼承
虛繼承實際上就是采用了虛基類表和指向虛基類表的指針。
class Base {
public:
char ch;
};
class A: virtual public Base {
public:
char a;
};
class B : virtual public Base {
public:
char b;
};
class Derive : public A, public B {
public:
char d;
};
記憶體分布如下:
從記憶體分布可以看到,vbptr是指向虛基類表的指針(占4個位元組),虛基類表儲存了虛基類與本類的偏移值,例如A::[email protected]中第二行中的第二個值8就是虛基類Base在本類A中的偏移值,可以看到在A類中ch存儲的起始位置為8位元組。
在Derive類的記憶體分布中,可以看到記憶體中隻包含了一個ch,也就是隻包含一個Base類,兩個vbptr分别指向兩個虛基類表,第二個虛基類表中的偏移值為12,是因為12是虛基類儲存的位置相對于B類位置的偏移值。
是以,采用虛繼承方式的話,虛基類依舊儲存在繼承類中,但是隻存在一份,虛基類表儲存的是虛基類與直接繼承類在記憶體中的偏移值。