C++ 菱形虛擬繼承 與 指針偏移問題

首先在談到菱形虛擬繼承之前先說明一下菱形繼承

菱形繼承是多繼承的一種特殊情況(如下,畫渣勿噴):
C++ 菱形虛擬繼承 與 指針偏移問題

圖中 B C 兩個類都繼承了A類,而 B C 又都被 D類繼承
按照繼承的定義,派生類當中都包含了基類,而這時虛擬繼承這種情況就會產生問題

<a>數據冗餘
首先是按照虛擬繼承的這種方法,D類當中就包含了兩份A類數據(分別來自B類和C類)
<b>二義性
其次是,當我們通過D類去訪問A類中的數據時,就會產生二義性,編譯器不知道是要去訪問D類中B中包含的A類數據還是C中包含的A類數據。

這時就要引入菱形虛擬繼承來了,但其實二義性的問題即使不使用菱形虛擬繼承也是可以解決的,相反數據冗餘不行:

class A{
public:
    int aa = 0;
};
class B : public A{
};
class C : public A{};
class D : public B, public C{};

int main(){
    D d1; 
    //d1.aa = 10;按照以上定義,如果運行這一句就會出現aa不明確的錯誤
    //若是像以下這樣顯示指定則沒有問題
    d1.C::aa = 10;
    d1.B::aa = 20;
    return 0;
}

但是既然數據冗餘的問題解決不了,我們還是需要使用菱形虛擬繼承,即如下代碼:

class A{
public:
    int aa = 0;
};
class B : virtual public A{};
class C : virtual public A{};
class D : public B, public C{};
int main(){
    D d1; 
    d1.aa = 10; //此時執行這一句就不會有問題
    return 0;
}

當aa複製成功後的監視窗口如下
C++ 菱形虛擬繼承 與 指針偏移問題
(D的對象內存模型↑)
沒有采用菱形虛擬繼承前,從內存模型中看可以很明顯看出存在兩份A的數據,自然會有數據冗餘和二義性的問題

但是菱形虛擬繼承從內存模型中看,B和C在虛擬繼承A時所存儲的不是A的數據,而是A的地址偏移量的地址,這兩個指針叫虛基表指針,兩個表叫虛基表。虛基表中存的就是偏移量。通過偏移量便可以找到下面的A。
流程就是:當訪問到A中數據時,首先通過偏移量地址找到相對當前位置A的地址偏移量,然後通過地址偏移量再找到A的數據,這樣就解決了兩大問題。

這裏有兩個需要關注的點:
一是虛擬繼承主要就是用來解決菱形繼承問題,其他地方不可亂用。
二是一般不建議設計出多繼承,一定不要設計出菱形繼承,否則程序在複雜性和性能上會有較大問題(多繼承可以認爲是C++的缺陷之一,所以後來很多語言都捨棄了多繼承,比方說JAVA)

====================================================================

現在再來說多繼承指針漂移的問題
先來看一段代碼

class A{
public:
    int aa = 0;
};
class B{
public:
    int bb = 0;
};
class C :public A, public B{
public:
    int cc = 0;
};

int main(){
    C p;
    A* a1 = &p;
    B* b1 = &p;
    C* c1 = &p;
    cout << a1 << ' ' << b1 << ' ' << c1 << endl;
    if (b1 == c1){
        cout << "b1 = c1" << endl;
    }
    return 0;
}

不瞭解這個問題的人,肯定會覺得a1,b1,c1三個指針的值應該是一樣的,但事實上↓
C++ 菱形虛擬繼承 與 指針偏移問題

這個問題就很玄乎了,賦的是同樣的值,爲什麼b1和c1的值會不同呢?但是爲什麼下面的if判斷卻又判斷他兩相同呢?

首先是第一個問題,導致地址不同的原因是 在C進行繼承時有一個先後順序(按照代碼的編寫順序),他會先繼承A類,再繼承B類,最後再實現自己的部分,這就導致了,a1指向了C對象中A的位置,b1指向了C對象中B的位置,從而導致了地址不同。

但爲什麼 if 判斷會通過呢?這是第二個問題,當判斷 ‘==’ 時,會分析兩個地址是不是指向了同一個實例對象,如果是,就會做隱式的類型轉換 ,然後再判等(雖然地址不同但指向的是同一個實例對象)。這就導致這樣的結果。

多繼承指針漂移和虛擬繼承都是C++繼承部分需要知道的點,最好還是加以記憶。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章