日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

詳解C++中菱形繼承的原理與解決方法_C 語言

作者:謎一樣的男人1 ? 更新時間: 2023-03-29 編程語言

菱形繼承形成原因

多繼承,呈菱形狀

菱形繼承代碼:

class A 
{
public:
    A() {}
    int _a ;
};
class B :public A 
{
public:
    //會存在父的int a;
    B() {}
    int _b ;
};
class C :public A 
{
public:
   //會存在父的int a;
    C() {}
    int _c ;
};
class D :public B,public C 
   
{
public:
   //會存在父的int a;    問題來了這個a是  父親b中的a 還是 父親c中的a -- 二義性;
    
   //父的int b;
   //父的int c;
    D() {}
    int _d ;
};
int main()
{
    
   
    D d;
    d.B::_a = 1;
    d.C::_a = 2;
    d._b = 3;
    d._c = 4;
    b._d = 5;
    cout << d._a << endl;//報錯  提示b.a具有二義性
    return 0;

}

出現二義性變量的內存布局

可以看到上圖中紫色框兩部分,是出現二義性的兩份A::_a變量,編譯器無法自主確定需要用哪一個,可以d::A_a 或者 d::B_a這樣使用;

應對方案

虛繼承 vitrual

vitrual修飾使派生類出現二義性的父類繼承部分(菱形的腰部)

class A 
{
public:
    A() {}
    int _a ;
};
class B :virtual public A //virtual修飾
{
public:
    //父的int a;
    B() {}
    int _b;
};
class C :virtual public A  //virtual修飾
{
public:
   //父的int a;
    C() {}
    int _c ;
};
class D :public B,public C 
   
{
public:

    //自己的 int a;(二義性的父親繼承經virtual修飾)
   //父的int b;
   //父的int c;
    D() {}
    int _d;
};
int main()
{
    
    D d;
    d.B::_a = 1;
    d.C::_a = 2;
    d._b = 3;
    d._c = 4;
    b._d = 5;
    cout << d._a << endl;//沒問題,輸出2,這個2是D類成員通過繼承,使得d自己獨有且只有一份的 A::_a成員變量;
    return 0;
}

解決二義性變量內存布局–虛基表

可以看到原先存放兩個二義性數據A::a和B::a的位置,變成了一個地址?

這個兩個地址就叫虛基表指針

通過對虛基表的進一步內存研究,發現了虛基表緊著得下一個位置存放了一個偏移量,這個偏移量是存放該虛基表指針的內存位置,與當前派生類獨有一份的成員變量_a之間的偏移長度;

這樣我們直接使用d::a的時候,因為獨有一份,也不會出現數據二義性的問題了;

注意,這和多態中的虛表(虛函數表兩回事)

其次,虛基表的指針,經過測試,也是某個多繼承派生類的多對象共用的;

eg: D d1,d2; 其中d1,d2兩個對象 上圖的這兩張虛基表指針是一樣的,原因很簡單,D類類型都一樣,那么某個位置起,到另一個相對位置偏移量肯定是固定的!

(同類型的虛表指針也如此,復用節省空間嘛)

感悟

繼承,多態無疑為我們創造了很多的價值,但是像菱形繼承這種弊端也是存在的,本質是多繼承而引起的問題,在一些語言中禁止了多繼承的行為,總之有利有弊,雖然C++允許了多繼承,但還是盡量別寫多繼承這種模式,復雜度和出現問題的概率都很大;

關于代碼復用等的另一種關系-組合

繼承是每個派生類都能相對于繼承is-a的關系; 每個派生類對象都是一個基類對象; 耦合度高

// Car和BMW Car和Benz構成is-a的關系
class Car{
    protected:
    string _colour ="白色"; // 顏色
    
    string _num ="陜ABITB";// 車牌號
};

class BMW:public Car{
    public:
    void Drive() {cout <<"好開-操控"<< end1;}
    };
    
    class Benz : public Car{
        public:
        void Drive() {cout <<"好坐-舒適"<< endl;}
        };

優先考慮組合has-a的關系; eg: 汽車-輪胎,has-a,組合(class 輪胎 作為class 車的成員嵌套) 耦合度低

// Tire和Car構成has-a的關系

class Tiref{
    protected:
    string _brand = "Michelin";// 品牌
    size_t size = 17;// 尺寸
    
};
class Car{
    protected:
    string _colour ="白色";// 顏色
    string _num ="陜ABITO";// 車牌號
    Tire _t;// 輪胎

};

原文鏈接:https://blog.csdn.net/wtl666_6/article/details/128751329

欄目分類
最近更新