在c++裏面有很大一個重點就是繼承和多態了,今天就先說一下關於繼承的一個小方面。因爲在c++當中的話繼承有
1.單繼承:一個子類只有一個直接父類時稱爲這個繼承關係爲單繼承
2.多繼承:一個子類有兩個或以上的父類時稱這個繼承關係爲多繼承
例如:單繼承
class AA
{
public:
int _aa;
};
class BB :public AA
{
public:
int _bb;
};
這是一個典型的繼承雖然他什麼也沒有,這裏BB繼承了AA
多繼承:
class AA
{
public:
int _aa;
};
class BB
{
public:
int _bb;
};
class CC : public AA, public BB 。。。
{
public:
int _cc;
};
這裏的CC同時繼承了AA和BB等,不過正是因爲有了多繼承所以同時帶來了一個問題,就是在繼承中有時會出現一種特殊的情況就是菱形繼承
class AA
{
public:
int _aa;
};
class BB : public AA
{
public:
int _bb;
};
class CC : public AA
{
public:
int _cc;
};
class DD : public BB, public CC
{
public:
int _dd;
};
這裏BB繼承了AA,CC也繼承了AA,但是DD同時繼承了BB和CC,更直觀一點的就是
這樣子的話很明顯在DD中就會有兩份AA中的函數和變量,這樣就會帶來二義性和數據冗餘的問題,那麼就得想辦法解決這些問題了
首先一種就是我們可以聲明我們具體是給哪一個父類中的變量賦值,例如
int main()
{
DD d;
d.BB::_aa = 10;
d.CC::_aa = 20;
return 0;
}
這樣就可以賦值成功,不過這樣只是解決了二義性的問題,數據冗餘的問題並沒有解決,所以又引入了一個新的解決辦法,叫做虛繼承,其關鍵字爲virtual;用法如下:
class AA
{
public:
int _aa;
};
class BB :virtual public AA
{
public:
int _bb;
};
class CC :virtual public AA
{
public:
int _cc;
};
class DD : public BB, public CC
{
public:
int _dd;
};
這個時候再對對象d中的_aa賦值
int main()
{
DD d;
d._aa = 10;
d._aa = 20;
return 0;
}
通過調試我們發現
當d中的_aa發生變化是不管是BB中_aa還是CC中的_aa都發生了改變,不過再往深的話,我們從監視窗口中並看不出來什麼了,我們不清楚他到底是怎麼實現的,所以我們只能用另一種方法,即內存來研究它是怎麼實現的,來解決二義性和數據冗餘的。
首先是不加virtual的,我們看內存可以得到其對象模型爲
然後是虛繼承的情況
根據其中地址得到其位置的下方存了一個數據,並且這個數據根據觀察可以看出來其數據是一個偏移量,並且是當前指針位置到存AA的位置的偏移量;
再來看其切片賦值的時候
發現在給其父類b賦值的時候是連這其指針和偏移量都一起切片賦值過去的
由此我們可以得到幾個結論:
1.在虛繼承中是把公共的數據重新在內存的最後開闢空間存進去,在其原來的位置存放一個指針,指針所指的位置是關於其到公共數據的偏移量。
2.虛繼承是有代價的,並且在其切片賦值的時候,不只是把其數據賦值回去,而是和着其虛繼承之後的指針,偏移量等等一起賦值的
3.虛繼承在查找數據的時候先要拿指針找偏移量然後再找其數據,犧牲了性能方面,是典型的時間換空間的做法