C++菱形继承与虚拟继承

C++的继承中有一种复杂的继承方式,这就是菱形继承。

菱形继承

(1)什么是菱形继承?

假设现在有四个类,分别是A、B、C、D四个类。如果B类和C类同时继承于A类,并且D类又同时继承于B类和C类,那么这四个类之间的关系就叫做菱形继承。可以用下面的图来表示。
在这里插入图片描述

(2)菱形继承的问题

菱形继承会存在两个问题,一个是数据冗余,另外一个是二义性。
我用一段简单的代码来说明这两个问题:

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;
};

1. 为什么说会存在数据冗余?
首先B类和C类都继承了A类,所以B类和C类中都一定包含A类的成员变量a,也就是说B类中有一个int a,同样C类中也有一个int a。那么当D类继承B类和C类后,那么在D类中一定包含两个int a,其中一个来自B类,另外一个来自C类。这就是所谓的数据冗余。
2. 为什么说会造成二义性?
根据上面的分析,D类中存在两个int a。所以当我们想要用D类的对象访问int a时,那么编译器就不知道究竟访问的是哪一个a。究竟访问的是从B类继承的a,还是访问的是从C类继承的a,这就是所谓的二义性。当然对于二义性我们可以用“类名::”的方式来解决。如下所示:

cout<<d1.B::a<<endl;   //这访问的继承于B类的a(其中d1是D类的对象)
cout<<d1.C::a<<endl;   //这访问的继承于C类的a(其中d1是D类的对象)

但是这样的办法却解决不了数据冗余的问题,所以就有了虚拟继承。

虚拟继承

虚拟继承不仅解决了菱形继承中的数据冗余问题,还解决了二义性问题。

(1)虚拟继承的方式

我在上面的代码上进行一些修改,这样可以增加对比性。

class A
{
   public:
		int a;
};
class B:virtual public A       //仅仅是比之前多加了一个关键字virtual
{
   public:
		int b;
};
class C:virtual public A      //仅仅是比之前多加了一个关键字virtual
{
   public:
		int c;
};
class D:public B,public C
{
   public:
		int d;
};

解析:
在上述代码中,当B类和C类从A类派生出来时,我们使用virtual关键字将A类声明为虚基类。这样从B类和C类派生词来的子类D,在继承时只是继承了A类的一份,也就是只保留了A类的成员变量a一份。于是便很好的解决了数据冗余和二义性问题。

(2)虚拟继承的原理

我结合下面一段代码来具体解释。

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 = 2;
	 d._b = 3;
	 d._c = 4;
	 d._d = 5;
	 return 0;
}

首先看下面这张图:这张图片是没有使用虚拟继承时候的内存情况
在这里插入图片描述
在没有使用虚拟继承时,在内存中从上到下分别存储了从B类中继承下来的成员(包括int _a 和int _b)、从C类中继承下来的成员(包括int _a和int _c)以及D类中新增的成员(int _d)。

下面这张图是使用了虚拟继承的内存状态图:
在这里插入图片描述
使用了虚拟继承之后,在B类和C类的同名成员的位置发生了改变,由原来的具体的值变成了一个地址,这其实就是一个指针,我们把这个指针叫做虚基表指针。而这个地址表示的就是一个虚基表的首地址。而在虚基表中存放的是内容是:当前位置相当于公共父类成员的偏移量。
举个例子:
假设现在要访问D类中的成员变量int a。那么编译器首先会找到该类对象的内存空间,然后在此空间寻找a的位置,但是当他找到a的位置时,发现原本应该是具体数值结果变成了一个地址,也就是说原本的值被一个虚基表指针所取代。然后编译器会根据这个虚基表指针找到这个虚基表的首地址,然后根据虚基表中的内容,最后才找到实际上a的具体地址,然后访问a

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章