什么是虛繼承
子類在繼承父類時,在聲明前加virtual關鍵字,這就是虛繼承
例:
class A { //...... } class B:virtual public A { //....... }
什么時候需要使用虛繼承
在多重繼承時,正常情況下子類對象將擁有繼承鏈上所有父類對應的部分,但是如果兩個父類都繼承了同一個類,
那么在子類將擁有兩份祖父類的數據,比如菱形繼承:
下面我以四個簡單類來分析其內存結構:
class CFuniture { int m_nFun; public: CFuniture() { m_nFun = 0; } virtual ~CFuniture() { } virtual void FunitureName() { cout << "CFuniture::FunitureName()" << endl; } }; class CSofa : public CFuniture { int m_nSofa; public: virtual void Sit() { cout << "CSofa::Sit()" << endl; } CSofa() { m_nSofa = 1; } virtual ~CSofa() { cout << "~CSofa()" << endl; } }; class CBed : public CFuniture { int m_nBed; public: CBed() { m_nBed = 2; } virtual void Sleep() { cout << "CBed::Sleep()" << endl; } virtual ~CBed() { } }; class CSofaBed :public CSofa, public CBed { int m_nSofaBed; public: CSofaBed() { m_nSofaBed = 3; } virtual ~CSofaBed() { cout << "~CSofaBed()" << endl; } virtual void SleepAndSit() { cout << "CSofaBed::SleepAndSit()" << endl; } virtual void SitAndSleep() { cout << "CSofaBed::SitAndSleep()" << endl; } virtual void Sleep() { cout << "CSofaBed::Sleep()" << endl; } virtual void Sit() { cout << "CSofaBed::Sit()" << endl; } }; int main(int argc, char **argv) { CSofaBed sofabed; return 0; }
這里家具類CFuniture祖父類,沙發類CSofa和床類CBed都繼承繼承自CFuniture,沙發床類CSofaBed同時繼承
了CSofa和CBed,現在來看看CSofaBed對象的內存布局:
可以看出祖父類CFuniture在CSofaBed中有兩份,這並非我們本意,我只想CSofaBed類對象中只有一份CFuniture
類數據,那么可以在CSofa和CBed繼承CFuniture時,加上virtual關鍵字表示和其它類共享CFuniture:
class CSofa :virtual public CFuniture class CBed :virtual public CFuniture
此時再來看看CSofaBed對象的內存布局:
從監視窗口可以看出CFunitrue類的數據有獨立的一部分,但是CSofa和CBed中依舊有CFunitrue類的數據,
其實這是VS的監視窗口為了更直觀的顯示繼承體系,在CSofa和CBed中顯示了CFunitrue類的數據,但是從
內存窗口中可以看出CFunitrue類的數據只有一份;
從上圖可以看出CFunitrue類在CSofaBed子類中被獨立出來,放在了對象的最尾部內存地址處,因為在CSofa和CBed以及CFunitrue中都有自己的虛函數,所以CSofaBed子類此時擁有三張屬於自己虛表(和父類中的虛表不是同一個),另外CSofaBed子類自己新加的虛函數被追加到繼承自CSofa虛表的后面,CSofaBed的析構函數被放到
繼承自祖父類CFunitrue的虛表中
還可以從內存布局中看出CSofaBed的兩個直接基類的虛表指針后面,還跟着兩個指針:0x01259b68和0x01259bbc
這兩個指針是偏移指針,它們兩指向相同的結構體,該結構體共8字節,前四個字節無意義,后四個字節記錄了偏移
指針距離CFunitrue類部分的字節數;從上圖中可以看出sofbed對象中,CFunitrue類的部分位於0x0044FBDC
開始的,CSofa類部分的偏移指針在0x0044FBC0處,兩者之間間隔了0x18個字節;CBed類部分的偏移指針位於0x0044FBD0處,和CFunitrue類的部分間隔了0x0C個字節