C++ 继承:菱形继承及菱形虚拟继承

目录

一. 菱形继承

1.1 什么是菱形继承

1.2 菱形继承体系的数据存储模型

1.3 菱形继承体系中存在的缺陷

二. 菱形虚拟继承

2.1 菱形虚拟继承的作用及定义方法

2.2 菱形虚拟继承中的数据存储模型


一. 菱形继承

1.1 什么是菱形继承

要明白什么是菱形继承,首先要明确单继承和多继承的概念:

  • 单继承:每个子类只有一个直接父类的继承体系。
  • 多继承:一个子类有两个或两个以上父类的继承体系。
C++ 继承:菱形继承及菱形虚拟继承_第1张图片 图1.1 单继承和多继承的结构图

菱形继承其实就是一种特殊的多继承,在菱形继承体系中,某一个子类含有两个或两个以上的父类,这个子类的父类又共同继承另外一个类。

C++ 继承:菱形继承及菱形虚拟继承_第2张图片 图1.2 菱形继承体系结构示意图

 演示代码1.1:(图1.2所示菱形继承体系的实现)

class A
{
public:
	int _a1;
};

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

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

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

1.2 菱形继承体系的数据存储模型

在演示代码1.1的继承体系中,D对象中包含C对象和B对象,B、C对象中又都包含一份A对象,为了能通过调试观察D类对象中成员变量数据在内存中的存储,编写了演示代码1.2,调试代码,打开内存监视窗口监视。基于内存监视窗口的结果,我们总结出了如图1.4所示的菱形继承数据存储模型。

演示代码1.2:

int main()
{
	D d;

	d.B::_a1 = 1;
	d._b1 = 2;

	d.C::_a1 = 3;
	d._c1 = 4;

	d._d1 = 5;

	return 0;
}
C++ 继承:菱形继承及菱形虚拟继承_第3张图片 图1.3 菱形继承体系派生类对象在内存中的存储
C++ 继承:菱形继承及菱形虚拟继承_第4张图片 图1.4 菱形继承体系中的数据存储模型

1.3 菱形继承体系中存在的缺陷

  • 二义性的问题

如图1.2所示的菱形继承体系,D类对象中会存储两份_a1成员,如果试图直接通过d._a1来访问_a1成员变量,则无法确定是B类对象还是C类对象中的成员变量。二义性问题,可以通过指定作用域限定操作符::指定作用域来避免。具体的语法格式为:d.B::_a1;

  • 数据冗余问题

由于d中存有两份_a1,但大部分情况下菱形继承中只需要有一个_a1变量即可,另外一个是多余的,这里可以使用虚继承来解决问题,在定义类B和类C时,使用virtual关键字声明虚继承来避免数据冗余问题。

二. 菱形虚拟继承

2.1 菱形虚拟继承的作用及定义方法

在第1.3章节中提到,直接使用菱形继承会存在数据冗余的问题,而使用菱形虚拟继承的作用就是避免数据冗余。同时,菱形虚拟继承还能避免二义性的问题,使用d._a1不存在不知道访问那个_a1的情况。

总结菱形虚拟继承的作用:

  1. 避免数据冗余,使派生类对象中名称相同的变量存储一份。
  2. 避免二义性,类对象中没有了同名变量,使用d._a1访问某个成员变量就不存在无法明确访问谁的问题。

虚拟继承定义方法:在菱形继承体系中的腰部类,即两个或两个以上继承同一父类的类的位置处,使用virtual声明虚拟继承即可。

C++ 继承:菱形继承及菱形虚拟继承_第5张图片 图2.1 菱形继承中声明virtual的位置

演示代码2.1:定义菱形虚拟继承的方法

class A
{
public:
	int _a1;
};

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

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

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

2.2 菱形虚拟继承中的数据存储模型

调试演示代码2.2,打开内存监视窗口观察数据在内存中的存储情况。

演示代码2.2:

int main()
{
	D d;

	d.B::_a1 = 1;
	d.C::_a1 = 2;
	d._b1 = 3;
	d._c1 = 4;
	d._d1 = 5;

	return 0;
}
C++ 继承:菱形继承及菱形虚拟继承_第6张图片 图2.2 菱形虚拟继承内存存储情况示意图

通过观察图2.2,我们可以发现,B和C的公共部分A被提取到了最下方的位置,这个A同属于B和C,也可以单独使用d._a1来访问,在存储B和C的成员变量的同时存储了两个指针变量,这两个指针成为虚基指针,两个虚基指针各指向一张存储了B对象和C对象相对与A的偏移量的表,这张表称为虚基表,通过A相对于B、C存储位置的偏移量,来找到A。

C++ 继承:菱形继承及菱形虚拟继承_第7张图片 图2.3 菱形虚继承派生类存储模型

你可能感兴趣的:(C++从入门到精通,c++,开发语言)