C++的一个大坑:菱形继承 希望这篇文章能让你理解什么是菱形继承,以及菱形继承的注意事项
单继承与多继承
单继承:一个子类只有一个直接父类时称这个继承关系为单继承
多继承:一个子类有两个或以上直接父类时称这个继承关系为多继承
java语言不支持多继承,只支持单继承。
菱形继承:菱形继承是多继承的一种特殊情况
菱形继承会导致什么呢?
菱形继承的问题:从下面的对象成员模型构造,可以看出菱形继承有数据冗余和二义性的问题。在Child的对象中Person成员会有两份
比如:
class Person
{
public:
string _name; // 姓名
};
class Student : public Person
{
protected:
int _num; //学号
};
class Teacher : public Person
{
protected:
int _id; // 职工编号
};
class Child : public Student, public Teacher
{
protected:
string _obj; // 主修项目
};
Child obj;
obj._name;
//从空间上说是数据冗余、从访问角度上是二义性
运行结果是:
如果想要代码跑起来可以这样:指定类域去访问
Child obj;
obj.Student::_name = "张三";
obj.Teacher::_name="张老师";
虚拟继承
但是上述做法从根本上并没有解决数据二义性的问题,为了能够解决二义性,C++又引入了一个坑,虚拟继承。(在继承的类派生列表的访问说明符前加上virtual)
class Person
{
public:
string _name; // 姓名
};
class Student : virtual public Person
{
protected:
int _num; //学号
};
class Teacher : virtual public Person
{
protected:
int _id; // 职工编号
};
class Child : public Student, public Teacher
{
protected:
string _obj; // 主修项目
};
虚拟继承可以解决菱形继承的二义性和数据冗余的问题。如上面的继承关系,在Student和Teacher的继承Person时使用虚拟继承,即可解决问题。需要注意的是,虚拟继承不要在其他地方去使用
为了更清楚的明白菱形继承与菱形虚拟继承的关系,我们通过使用内存监视窗口来了解其数据存储方式(监视窗口编译器优化过,不是原汁原味了):
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;
};
我们先使用内存窗口观察菱形继承在内存中的存储状况:
D d;
d._b = 1;
d._c = 2;
d._d = 3;
d.B::_a = 4;
d.C::_a = 5;
菱形虚拟继承在内存中的分布情况:
D d;
d._b = 1;
d._c = 2;
d._d = 3;
d.B::_a = 4;
d.C::_a = 5;
d._a = 6;
我们对最后的地址分析:(内存地址跟上图不一样是因为重新运行了一次)
这个虚基表是用来干什么的?
菱形虚拟继承的内存对象成员模型:这里可以分析出D对象中将A放到的了对象组成的最下面,这个A同时属于B和C,那么B和C如何去找到公共的A呢?这里是通过了B和C的两个指针,指向的一张表。这两个指针叫虚基表指针,这两个表叫虚基表。虚基表中存的偏移量。通过偏移量可以找到下面的A
下面是上面的Person关系菱形虚拟继承的原理解释:
如有错误或者不清楚的地方欢迎私信或者评论指出🚀🚀