【C++】菱形继承

菱形继承:

继承与多态是C++中重要的概念

继承的基本概念:

派生类(子类)是 具有基类(父类)性质的特殊群体,比如人是父类,学生/老师各是一个子类。
- 继承是面向对象复用的重要手段
- 三种限定修饰符:public private protected
- 基类的私有成员是不能在派生类中被访问的,如果一些成员对象不想再基类中被直接访问,但可以在派生类中被访问,可以被定义为保护成员。保护成员限定符是因为基础而出现的。
- public是一个接口继承,保持is-a原则。每个父类的成员对子类都可用,每个子类的对象也是父类的对象。
- protected/private是实现继承,保持has-a原则,基类的部分成员并未成为子类的一部分。
- class默认继承方式是private,struct默认是public。

class A
{
    void f()
    {
        cout << "A::f()" << endl;
    }
public:
    int _a;
};
class B : public A
{
    void f()
    {
        cout << "B::f()" << endl;
    }
public:
    int _a;//与父类同名
    int _b;
};
int main()
{
    B b;
    b._a = 1;//就近原则 隐藏/重定义
    b.A::_a = 2;//域作用符
    return 0;
}

在is-a原则中,具有切片功能:子类可以赋值给父类,父类的指针可以指向子类。反之不可

int main()
{
    Person p;
    student s;
    p = s;
    Person* p1 = s;
    Person& r1 = s;
    return 0;
}

菱形继承


  • 单继承:一个子类只有一个直接继承的父类
  • 多继承:一个子类有多个直接继承的父类
  • 菱形继承:一个子类多继承的俩个父类又同时继承于同一个父类

所以在菱形继承中会存在一个问题:D会继承B/C成员,但B/C同时继承了A,所以在D中会有俩组A的成员,这就是 二义性数据冗余问题。
class A
{
public:
    int _a;
};

class B : virtual public A
{
public:
    int _b;
};

class C : virtual public A
{
public:
    int _c;
};

class D : public B, public C
{
public:
    int _d;
};
int main()
{
    D d;
    d.B::_a = 1;
    d.C::_a = 1;
    d._b = 2;
    d._c = 3;
    d._d = 4;
    return 0;
}

为了解决这个问题我们使用了虚继承

剖析虚继承

这里写图片描述
可以看出在虚继承前,a分别存放在B C中,所以D中有俩个a。但在虚继承后a存放在末尾,只有一个a解决了数据冗余二义性。BCD共用同一个a,B/C又分别通过各自的虚基表来找到a的存放地址,在B的存放区,第一存放的是虚基表的地址,通过他来找到虚基表,虚基表的第二行存放的是偏移量即到a的距离,通过偏移量就可以找到a。其中虚基表的第一行是到虚表指针的距离大小。

可以看出虽然通过virtual解决了菱形继承的数据冗余二义性问题,但是在内存的存放与寻找中特别麻烦,同时也伴随着空间布局存取时间的额外成本。这样会降低程序的效率,带来性能上的损耗,所以不到万不得已不要使用菱形继承。

猜你喜欢

转载自blog.csdn.net/qq_32672481/article/details/76135645
今日推荐