c++中的菱形继承与虚拟菱形继承

c++中的继承关系分为单继承和多继承

  • 单继承:一个派生类只有一个基类
  • 多继承:一个派生类不止有一个基类

在多继承的过程成容易造成二义性问题。

菱形继承是多继承中的一种复杂的情况。
c++中的菱形继承与虚拟菱形继承_第1张图片
菱形继承的有两个问题:

  • 二义性:当使用A的数据时无法确定继承自B 还是 C
  • 数据冗余:类D中会有两份类A的数据

先看一个菱形继承的例子

#include 
using namespace std;

class A{

public:
	int _a = 1;
};

class B : public A{

public:
	int _b = 2;
};

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

class D : public B, public C {
public:

	int _d = 4;
};

int main(){
	D d1;
	//d1._a = 100;

	d1.B::_a = 100;  //可以显示的指定访问那一个类中的成员,解决了二义性问题
	d1.C::_a = 101;

}

c++中的菱形继承与虚拟菱形继承_第2张图片
D中最后会出现两份_a,这就造成了数据冗余,为了解决数据冗余的问题,使用了虚拟继承

l菱形虚拟继承

通过菱形的虚拟继承,可以使该多继承体系中只有一份公共的A中的数据

//虚拟继承通过virtual关键字实现
class A{

public:
	int _a = 1;
};

class B : virtual public A{

public:
	int _b = 2;
};

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

class D : public B, public C {
public:

	int _d = 4;
};

在虚拟继承中通过使用虚基表,来实现虚拟继承不对原继承体系的原本继承关系造成影响。尽管现在只有一份公共的A,但原来的继承关系并不发生改变,只要继承了A都了能访问A的数据,我们通过它们的内存模型来看
c++中的菱形继承与虚拟菱形继承_第3张图片
通过虚基表我们可以发现,此时对象d1中只有一个A中的数据_a,不再出现冗余的两份_a,而原来存放继承自B中的_a的位置以及继承自C中的_a的位置,此时存放的是一个地址,找到该地址,发现其中存放的是数,而这个数就是一个偏移量,表示此时B和C的位置到达公共部分_a的偏移量。这两个地址叫做虚基表指针,而指向的就是虚基表,这两个虚基表中存放的是偏移量。

继承关系和组合关系

多继承体现了c++的语法复杂性,算是c++的一个设计缺陷,因为多继承的关系,所以有了菱形继承,从而出现了虚拟继承。从而导致了复杂度的上升,所以尽量少的使用多继承。

组合
class Legs{
 protected:
 size_t _size = 17; // 腿长

 };


class Person{
 protected:

 string _name ;//姓名 
 Legs _leg;
 };

继承体现的是设计层次结构中的复用,而还有一种对象间的服用关系,就是组合。

  • 继承表明了每一个派生类对象都是一个基类对象,是一种has-a的关系。

  • 组合关系表示,如果B组合A,表明每一个B对象中都有一个A对象,是一种is-a的关系。
    -优先使用组合关系,而不是继承关系,表明了一个对象是组成另一个对象的一部分。

  • 派生类与基类之间的关系又称为白箱复用,白箱是指可视性而言,在继承关系中基类内部的细节对于派生类来说是可见的,基类在一定程度上破坏了封装,基类的改变在很大程度上会影响派生类。所以继承关心间的关联性很强,耦合度很高。

  • 组合关系是一种黑箱复用,被组合的类细节在另一个类中是不不可见的,被组合的类只需要提供良好的接口即可。组合之间的关系的依赖程度低,耦合度低,更能保证封装的完好性。

  • 面向对象的设计中倾向于高内聚,低耦合的设计,所以继承和组合关系间,优先使用自合

你可能感兴趣的:(C++,菱形继承,虚拟继承)