【C++】菱形继承以及菱形虚拟继承

菱形继承和菱形虚拟继承

C++三大特性 继承 封装 多态

继承就是一种管理行为。为了使我们规范的去使用类里面的成员。把成员函数或者成员变量保护起来,必须通过对象去调用。
继承其实是类级别的复用对父类没有任何影响。子类将父类的成员都继承下来,只是关系发生了变化

菱形继承

单继承: 一个子类只有一个直接父类时,称这个继承关系为单继承
在这里插入图片描述
多继承:一个子类有两个或两个以上的直接父类时称这个继承关系为多继承

在这里插入图片描述
菱形继承: 菱形继承是多继承的一种特殊情况

在这里插入图片描述

菱形继承带来的问题: 从成员模型可以看出来,菱形继承有数据冗余数据二义性的问题。在最下面的一层D类中,对象会有2份最上层对象A类里面的成员
在这里插入图片描述

再来看一个例子,来看菱形继承所带来的问题

class Person
{
public:
	string name;
};
class Student : public Person
{
public:
 	string No;
};
class Teacher: public Person
{
public:
	string id;
};
class Course : public Student, public Teacher
{
public:
	string course;
};
int main ()
{
	//这样就会有二义性无法明确知道访问的是哪一个
	Course c;
	c.name = "tom";
	// 这样就可以解决二义性的问题, 但是数据冗余问题无法解决
	c.Student::name = "wang";
	c.Teacher::name = "lili";
}

可以看出:
虚拟继承可以解决菱形继承的二义性和数据冗余的问题。如上面的继承关系,在B类和C类的继承A类时使用虚拟继承,就可以解决菱形继承的问题。

菱形虚拟继承

菱形虚拟继承的概念
菱形虚拟继承就是在多个类同时继承一个类的时候加上virtual关键字,使得父类的变量在全局只有一份,多个继承父类的类可以同时找到它并修改它
作用就是: A类是父类, B,C类继承父类, D类继承B,C类。 那么A类的成员变量就在B和C类中,但是D类继承B,C类,A类的成员变量就在D类中有两份。菱形虚拟继承的作用是:使得A类的成员变量在对象中只有一份
菱形虚拟继承的做法
在多个类同时需要继承同一个父类的时候,在继承方式前加上virtual关键字

例子:

class Person
{
public:
	string name;
};
class Student :  virtual public Person   //学生类需要继承人这个类     加上virtual关键字,虚拟继承
{
public:
 	string No;
};
class Teacher: virtual public Person    //老师类也需要继承人这个类     加上virtual关键字,虚拟继承
{
public:
	string id;
};
class Course : public Student, public Teacher
{
public:
	string course;
};
int main ()
{
	Course c;
	c.name = "tom"; //把name改为tom,所有类里面的name全都该为tom
}

总结:这样子,就不会产生数据二义性了,和数据冗余了,因为是继承,所有所有类里面都含有基类变量name。但是菱形虚拟继承做的是在所有类里面的name变量都是同一个。所有你Course类里面的name改变,所有类成员里面的name变量都是"tom"

那么如何实现的呢??

虚拟继承的原理(虚拟继承解决数据冗余和二义性的原理)

我们可以借助内存窗口观察对象的成员模型。

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;
	cout << d.B::_a << endl; //输出2
	cout << d.C::_a << endl; //输出2
	d._b = 3;
	d._c = 4;
	d._d = 5;
}

程序结果是什么呢?
因为解决了菱形继承的问题,所以B对象中的_a和C对象中的_a都是同一个_a, 所以对_a最后一个赋值的是2, 所以都是2

如果在B和C继承A的时候,都不加上virtual关键字,那么就不会菱形继承了。 所以会有数据冗余的问题,所以第一个d.B::_a输出的是1,第二个d.B_a输出的就是2.

菱形继承的内存对象模型(继承的时候不加上virtual关键字)

在这里插入图片描述
我们可以看到,在B类和C类中第一个位置的变量都是A类中成员变量_a, D类的对象中包含了B类和C类的成员变量,那么_a就被包含了两次,已经有冗余了。

菱形虚拟继承的内存对象模型:

在这里插入图片描述

A是虚基类,A是被继承的。所以B和C都会有虚基表指针。虚基表指针指向虚基表,虚基表中存的是偏移量,通过偏移量可以找到虚基类中的成员变量

总结:
把虚基类A的成员a放在所有对象的最下面。 B类和C类的对象空间里面存放一个指针,叫做虚基表指针。指向一个虚基表。虚基表指针记录的是当前位置距离下面虚基类的偏移量。因为B类和C类都要找到它。就是通过偏移量找到它的,也就是通过偏移量找到a。

问题总结:

问题1: 什么是菱形继承?菱形继承带来的问题是什么?
问题2: 什么是菱形虚拟继承? 如何解决数据二义性的问题?

  1. 菱形继承是当有多个类(A,B)同时继承一个父类©时,同时也有一个类(D)同时继承这多个类的其中的(>=2)几个(A,B)。
    这样的话,C中含有的成员变量,在A和B中都会含有一份,D同时继承A和B,那么C中的成员变量就会在D中存在两份。
    所以菱形继承带来的问题是,含有重复的变量包含,会造成数据冗余和数据二义。

  2. 菱形虚拟继承就是在多个类同时继承一个类的时候加上virtual关键字,使得继承父类的变量在全局只有一份,多个类可以同时找到它并修改它。菱形虚拟继承是依靠虚基表来解决数据二义性问题的。通过virtual虚拟继承的类的栈中都会存一个虚基表指针,标识父类变量的偏移量,以找到父类变量。使得全局只有一个父类的变量

猜你喜欢

转载自blog.csdn.net/weixin_43939593/article/details/103655543
今日推荐