Bootstrap

谈谈C++:多继承&&虚拟继承

   谈之前我们先简单了解一下继承的概念

什么是继承?

继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。以前我们接触的复用都是函数复用,继承是类设计层次的复用。

简单来说,就是先有一个A类,我们需要对A进行扩展,增加功能,实现不同作用,就需要B继承A类。此时:A就是基类(父类),B就是派生类(子类),B就继承了A。

继承类型又分为单继承多继承

单继承 简单来说就只有一个父类继承
在这里插入图片描述

多继承 就是有 两个或多个父类继承
在这里插入图片描述

多继承

当我们使用多继承时,我们总会遇到许多问题,比如数据冗余和对象二义性问题等…
下面我们就用菱形继承作为例子
下面我们先看代码,来研究这些问题

class Person
{
public:
	string _name; // 姓名
};
class Student : public Person
{
protected:
	int _num; //学号
};
class Teacher : public Person
{
protected:
	int _id; // 职工编号
};
class Assistant : public Student, public Teacher
{
protected:
	string _majorCourse; // 主修课程
};
void Test()
{
	Assistant a;
	a._name = "peter";
}

我们用图像化表示
在这里插入图片描述

代码运行结果
在这里插入图片描述
这里就发生了一个错误:访问对象不明确

也就是说:在代码中有多个对象,不知道你要访问那个name

我们有两种解决方法:1.在当前a的后面指明访问对象的name,解决了数据的二义性但是仍然存在数据冗余的情况 。2.我们使用到虚拟继承,来解决上面的所有问题。

1.显示指定访问哪个父类的成员可以解决

在这里插入图片描述
运行通过,虽然这样解决了二义性问题,但是没有解决本质问题,这样就需要存两份,造成空间浪费,也就是说仍然存在数据冗余 (两个对象都储存了name的对象,下面有图解) 问题,所以这个不是最优解。

2.使用virtual虚拟继承来解决

代码演示

class Person
{
public:
	string _name; // 姓名
};
class Student : virtual public Person
{
protected:
	int _num; //学号
};
class Teacher : virtual public Person
{
protected:
	int _id; // 职工编号
};
class Assistant : public Student, public Teacher
{
protected:
	string _majorCourse; // 主修课程
};
void Test()
{
	Assistant a;
	a._name = "peter";	//解决数据二义性以及数据冗余
}

成功运行
运行通过,解决数据二义性和数据冗余这两个问题。

为什么它能解决数据二义性以及数据冗余的?

我们再引用一个代码来演示其如何解决数据二义性以及数据冗余。

class A
{
	public:
		int _a;
};

// class B : public A
class B : virtual public A	//虚拟继承父类
{
	public:
		int _b;
};
// class C : public A
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;
}

我们通过vs调用内存来查看他们的分布

1.多继承的内存分布

数据冗余

我们发现B,C对象中都存在A对象,这样的两次A就造成了数据冗余的情况。

2.虚拟继承的内存分布

虚拟继承

这里可以分析出D对象中将A放到的了对象组成的最下面,这个A同时属于B和C,那么B和C如何去找到公共的A呢?这里我们发现,是通过了B和C的两个指针,指向的一张表。这两个指针叫虚基表指针,这两个表叫 虚基表 。虚基表中存的偏移量。通过偏移量可以找到下面内存分布的唯一A。这种情况就解决了数据冗余和数据二义性的问题。

下面是上面的Person关系菱形虚拟继承的原理解释:
解释

继承总结与反思

  1. 很多人说C++语法复杂,其实多继承就是一个体现。有了多继承,就存在菱形继承,有了菱
    形继承就有菱形虚拟继承,底层实现就很复杂。所以一般不建议设计出多继承,一定不要设
    计出菱形继承。否则在复杂度及性能上都有问题。
  2. 多继承可以认为是C++的缺陷之一,很多后来的语言都没有多继承,如Java。

谢谢大家

;