【C++】菱形继承

来源:互联网 发布:网络情歌对唱经典老歌 编辑:程序博客网 时间:2024/04/28 09:41

菱形继承:

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

原创粉丝点击