C++的虛繼承主要解決了數據冗餘與二義性的問題,起實現方法是什麼呢,我們先看一段代碼。
#include<iostream>
using namespace std;
class A
{
public:
int _num;
};
class B1 : public A
{
};
class B2 : public A
{
};
class C : public B1, public B2
{
};
int main()
{
C c;
cout << sizeof(c) << endl;
c.B1::_num = 1;
c.B2::_num = 2;
cout << "c.B1::_num:" << c.B1::_num << endl;
cout << "c.B2::_num:" << c.B2::_num << endl;
cout << "c._num:" << c._num << endl;
return 0;
}
這是一個菱形繼承,直接訪問兩個域中的_num並賦不同值時,兩個域中的_num不會是同一個數值,證明了c中含有兩個_num成員變量。當我們訪問C域的_num,系統無法識別到底是訪問B1所繼承下來的還是B2所被繼承下來的,這裏編譯器會報錯,這裏c的大小爲8個字節,調試點開監視窗口我們可以看到:
一個C在不同域裏繼承了兩份A類的成員變量,這樣就造成了數據冗餘及二義性。
如何解決這個問題呢,C++中引入了關鍵字“vritual”在一代繼承時加入關鍵字,修改後我們的代碼爲:
#include<iostream>
using namespace std;
class A
{
public:
int _num;
};
class B1 : virtual public A
{
};
class B2 : virtual public A
{
};
class C : public B1, public B2
{
};
int main()
{
C c;
cout << sizeof(c) << endl;
c.B1::_num = 1;
c.B2::_num = 2;
cout << "c.B1::_num:" << c.B1::_num << endl;
cout << "c.B2::_num:" << c.B2::_num << endl;
cout << "c._num:" << c._num << endl;
return 0;
}
這時我們發現通過c我們不但可以訪問到自己域的成員變量,還可以訪問到所繼承類的成員變量,並且,當給不同域的同名成員變量賦值之後,它們的外在表現都是同一個值,這樣節解決了二義性與數據冗雜,爲什麼說是外在表現呢,當我們打開調試窗口與內存時我們會看到:
調試窗口:
調試窗口中,c不但包含兩個父類的成員變量,還包含兩個父類共同的父類的成員變量,這時c的大小爲12。更驚奇的是,無論我們通過那個域名對其成員變量進行賦值,其他域的成員變量的值也會隨之改變,這時爲什麼呢。這裏我們打開內存監視窗口:
這裏我們可以看到,c中存的並不是真正的三個類中的成員變量,而是兩個指針,與一個成員變量,拿着兩個指針究竟指向的是什麼呢,我們再打開更多的內存窗口進行觀察:
第一個指針:
第二個指針:
我們可以發現,這兩個指針都爲空,但是,在這兩個指針底下,卻有兩個數字,這兩個數字又恰好與存放這兩個地址內容所在地址到存放c成員變量所在地址的偏移相同。由此可以看出VS下實現virtual關鍵字解決菱形繼承的二義性與數據冗餘的方法是,通過指針的偏移,使c中所存在的三塊空間,在直接或間接的條件下,指向同一塊空間。
未完待續
本文出自 “pawnsir的IT之路” 博客,請務必保留此出處http://10743407.blog.51cto.com/10733407/1750123