上一篇文章,我大概说了下:虚继承概念。
C++:类和对象:继承_hongwen_yul的博客-CSDN博客
这篇问题,我们重点分析虚继承底层原理。
C++语法——详解虚继承_就要 宅在家的博客-CSDN博客_c++ 虚继承
案例:假设有如下代码
#include<string>
#include<iostream>
using namespace std;
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.a = 1;
d.b = 2;
d.c = 3;
d.d = 4;
return 0;
}
1:先看图(如果上述代码不采用虚继承)
此时可以看到D类实例化对象内部结构如图所示
很显然 至少面临两个问题
1:基类A中的 成员变量 int a , 在子类B和子类C中各自存在一份,这显然是冗余的
2:基类D在调用基类变量 a时(D::a),还会提示:D::a 不明确(这里不明确的意思就是不知道调用子类B的还是调用子类C的)
2:修改成虚继承模式
显然通过虚继承,很好的解决了上面两个问题。
3:虚继承原理
在上图中,父类数据并不存放在 虚继承的子类中,那么子类是怎么找到父类的数据的了?
1:在虚继承的类中,会定义一个虚基表指针vbptr ,这个指针指向虚基表
2:在虚基表中存在偏移量,这个亮就是表的地址到 父类数据地址的距离,通过这个距离,子类就可以找到唯一的 成员。
4:通过调试来分析
先查看对象d的 内存分布
上图我们引出了 虚基表指针
现在我们再通过内存窗口,查看一下虚基表指针指向的地址。
这就解释了,为什么 bptr 和 cptr (虚基表指针)能找到并不位于自己内部的变量a ?
因为通过 bptr和cptr 去寻找 变量a 时,会从自身的虚基表指针中找到虚基表,通过虚基表保存的偏移量就可以找到变量a 地址,从而找到变量a。
简化下就是下图
5:使用虚继承需要注意的事情
当使用虚继承的时候,需要注意,虚继承只有在多继承时才有用,也就是说如果只有一层继承关系或者是单继承都将不起作用。
因为虚继承时保证子类中只有一个间接父类,即虚继承只能在隔代继承中起作用。
比如:下面这两种情况即便使用虚继承也是没意义的。
第一种情况:虽然 class B虚继承产生了 虚基表和指针,但是 class B并 没有子类,而虚继承是用以保证子类只有一个间接父类 class A 。而本例 class B 并没有子类。
第二种情况:Class C虚继承了 Class B,但是 class B是 class A的非继承类,那么class B 就存在一份 A 。 Class D 对A 是虚继承(保存指向A的虚基表),这就导致 class E 在实例化时存存放一个 对D而言公共的 A 。 这样导致 class E 中还是存放两个 A ,那么调用变量就会出现混乱。
所以正确的继承关系应该是:当 Class A的子类继承它时,应该都定义成虚继承,这样才能保证当有像 Class E 这样的间接子类定义时,成员才会在公共区域只有一份。