有关继承的那点事丶

所谓继承,就是从先辈得到的属性和行为特征。类的继承就是新的类从已有类那里得到已有的特征。从另一个角度来看问题,从已有类产生新类的过程就是类的派生。类的继承和派生机制使程序员无须修改已有的类,只需在已有类的基础上,通过增加少量代码或者修改少量代码的方法得到新类,从而较好地解决代码重用的问题。
继承又分为:单继承丶多继承和菱形继承
单继承:一个子类只有一个直接父类时称这个继承关系为单继承。
多继承:一个子类有两个或以上直接父类时称这个继承关系为多继承.
菱形继承:多个类继承了同一个公共基类,而这些派生类又同时被一个类继承。
这里写图片描述
然而菱形继承也存在一些问题:

class A
{
public:
    int _a;
};

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

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

class D: public B, public C
{
public:
    int _d;
};

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

cout<<sizeof(B)<<endl;
    B b;
    b = d;
    system("pause");
    return 0;
  }
}

这里写图片描述
由图可见:菱形继承存在二义性和数据冗余问题 class A在类中有两个,这可能不是我们想要的结果,增加调用的困难,同时也会浪费内存资源
为了解决上述问题我们可以采用虚继承的方式

#include<iostream>
using namespace std;

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 = 0;
    d._b = 1;
    d.C::_a = 2;
    d._c = 3;
    d._d = 4;

    cout << sizeof(D) << endl;
    system("pause");
    return 0;
}

这里写图片描述
通过内存我们发现此时_a的对象只有一个就是C,这样二义性就解决了,同时我们发现派生类B中的_a消失,这样就解决了数据的冗余的问题。
这里写图片描述
这里写图片描述
第一个偏移20个字节刚好到基类AA,第二个偏移12个字节刚好到基类AA.
但调试后最终结果为24 这是因为多出来的字节要用于存放偏移地址。这也是虚继承未解决问题所付出的代价。
总结:
1. 虚继承解决了在菱形继承体系里面子类对象包含多份父类对象的数据冗余&浪费空间的问题。
2. 虚继承体系看起来好复杂,在实际应用我们通常不会定义如此复杂的继承体系。一般不到万不得已都不要定义菱形结构的虚继承体
系结构,因为使用虚继承解决数据冗余问题也带来了性能上的损耗。

猜你喜欢

转载自blog.csdn.net/important_/article/details/75949293