問題引入:
如果有Base類,B1類,B2類,D類,如下圖繼承關係
那麼按一般的繼承來看,D類創造的對象會繼承B1類的方法與成員,同時也會繼承B2類的方法與成員;
接下來類B1 , B2 會分別去繼承Base類的方法與成員,那麼D類的對象在調用Base類的方法時,到底是繼承B1類這邊的Base,還是會繼承B2類這邊的Base,此時就會產生二義性
爲了解決這個問題,就看看虛擬繼承是怎麼來解決這個二義性問題的
菱形的虛擬繼承的源代碼
#include <iostream>
using namespace std;
class Base
{
public:
Base()
{
cout << "Base() " << this << endl;
}
~Base()
{
cout << "~Base() " << this << endl;
}
int m_data1;
};
class B1 : virtual public Base
{
public:
B1()
{
cout << "B1() " << this << endl;
}
~B1()
{
cout << "~B1() " << this << endl;
}
int m_data2;
};
class B2 : virtual public Base
{
public:
B2()
{
cout << "B2() " << this << endl;
}
~B2()
{
cout << "~B2() " << this << endl;
}
int m_data3;
};
class D : public B1, public B2
{
public:
D()
{
B1::m_data2 = 0x1;
B1::m_data1 = 0x2;
B2::m_data3 = 0x3;
B2::m_data1 = 0x4;
m_data4 = 0x5;
cout << "D()" << this << endl;
}
~D()
{
cout << "~D() " << this << endl;
}
int m_data4;
};
void Test()
{
D d;
//cout << sizeof(D) << endl; //24
}
int main()
{
Test();
getchar();
return 0;
}
2.斷點打到D類的構造函數這條語句B1::m_data2 = 0x1;運行完D類構造函數後調試。
內存1中的地址欄輸入&d可以看到類D對象的模型(取&d因爲在Test()函數創建的是D類的d對象)
3.接下來問題就來了,明明是取D類對象d的地址,爲什麼這個地址下有存儲了地址,裏面到底是什麼東 西,那就看看吧,(可以看看反彙編和相應的內存)
此處可以看到它的地址偏移4個字節中存儲了0X 00 00 00 14
(這個數據有什麼意義哪?)
在看看反彙編就可以瞭解到編譯器到底在搞什麼鬼
看看在給m_data1賦值時,也就是給類Base數據賦值時,是什麼情況
—————————————————————————————————
第一步 取得this指針給寄存器eax
第二步 將寄存器eax中內容(即地址 (0x 00 B7 DC C8))給 ecx
第三步 將寄存器exc中內容加4 (即 地址(0x 00 B7 DC CC))後,取其地址內容給edx(內容爲 0X 00 00 00 14)
第四步 取this指針給寄存器eax
第五步 eax + edx 的結果爲 (0x 00 30 FD 60 + 0X 00 00 00 14 = 0x 00 30 FD 74),接下來將數據2
存放到這個地址下
——————————————————————————————————————
從模型圖可以看到地址(0x 00 30 FD 74)就是Base類的地址
爲什麼存放的是數據2,但是結果是4,這就要看清楚了,下面還有條語句未執行,
那就是 B2::m_data1 = 0x4;
這就是菱形虛擬繼承下爲了不產生二義性的奧妙
(D類繼承Base類的方法與成員,編譯器在內存中只開闢了一塊)