定義:兩個子類繼承同一個父類,而又有子類同時繼承這兩個子類。
如果直接繼承會引發訪問不明確(二義性),以及數據冗餘。如果直接指定訪問對象,可解決二義性(第一段代碼以及解析圖),而要解決數據冗餘,則要引入虛函數(第二段代碼以及解析圖)。
代碼一:
#include <iostream>
using namespace std;
class A
{
public:
int _a;
};
class B : public A
{
public:
int _b;
};
class C : public A
{
public:
int _c;
};
class D : public C, public B
{
public:
int _d;
};
int main()
{
D dd;
dd.C::_a =2;
dd._c = 4;
dd.B::_a =1;
dd._b = 3;
dd._d = 5;
return 0;
}
由上圖可以看出,繼承順序爲C B , D的繼承方式決定創建空間的先後。指定訪問對象可以解決二義性,卻無法解決數據冗餘。
代碼二:
#include <iostream>
using namespace std;
class A
{
public:
int _a;
};
class B : virtual public A
{
public:
int _b;
int _b1;
};
class C : virtual public A
{
public:
int _c;
};
class D : public B, public C
{
public:
int _d;
};
int main()
{
D dd;
dd.B::_a =1;
dd._b = 3;
dd._b1 = 6;
dd.C::_a =2;
dd._c = 4;
dd._d = 5;
return 0;
}
對比上圖,由於vptr的存在,空間加大,解決了數據冗餘的問題。下面爲實現過程
現在我們查看dd對象創建的過程。
1.進入反彙編
D dd;
010B4BC8 push 1
010B4BCA lea ecx,[dd]
010B4BCD call D::D (010B1442h) //dd創建
dd.B::_a = 1;
010B4BD2 mov eax,dword ptr [dd]
010B4BD5 mov ecx,dword ptr [eax+4]
010B4BD8 mov dword ptr dd[ecx],1 //寫入1
dd._b = 3;
010B4BE0 mov dword ptr [ebp-20h],3
dd._b1 = 6;
010B4BE7 mov dword ptr [ebp-1Ch],6
dd.C::_a = 2;
010B4BEE mov eax,dword ptr [dd]
010B4BF1 mov ecx,dword ptr [eax+4]
010B4BF4 mov dword ptr dd[ecx],2
dd._c = 4;
010B4BFC mov dword ptr [ebp-14h],4
dd._d = 5;
010B4C03 mov dword ptr [ebp-10h],5
return 0;
2.進入dd創建語句
D::D:(部分語句)
//之前語句對dd進行了初始化。
010B2D47 je D::D+3Ch (010B2D5Ch)
010B2D49 mov eax,dword ptr [this] //將dd首地址送往eax.
010B2D4C mov dword ptr [eax],10BCCCCh //將B距離A的偏移地址
//送到eax.
010B2D52 mov eax,dword ptr [this]
010B2D55 mov dword ptr [eax+0Ch],10BCCE8h //將C距離A的地址
//送到eax.
010B2D5C push 0
010B2D5E mov ecx,dword ptr [this]
010B2D61 call B::B (010B143Dh) //B的構造
010B2D66 push 0
010B2D68 mov ecx,dword ptr [this]
010B2D6B add ecx,0Ch //將ecx+B的大小
010B2D6E call C::C (010B1447h) //C的構造
010B2D73 mov eax,dword ptr [this]
進入B的構造(部分)
010B3049 mov eax,dword ptr [this]
010B304C mov dword ptr [eax],10BCC70h //存放B的大小
010B3052 mov eax,dword ptr [this]
進入C的構造部分
010B3119 mov eax,dword ptr [this]
010B311C mov dword ptr [eax],10BCCB8h //存放C的大小
010B3122 mov eax,dword ptr [this]
dd對象在創建的過程中,調用D的構造,然後對B和C進構造,在B構造時,在其起始位置(dd位置)放置一指向B的父類(A)的指針的偏移地址,偏移地址+4便是距離A的偏移量,同時在創建一偏移地址,保存B的對象的大小。然後dd加上B的對象的空間大小,在之後創建B的對象。B和A的對象的首地址指向同一空間(父類A)。
ps:對於偏移地址當前內容是爲其他偏移量預留,在菱形繼承和多態進行共同探究時發現那個位置存放的是距離this的偏移量。