在 C++ 多继承中,可能会出现 the most derived class 拥有多个 the most based class 的多份相同数据成员。比如说下面这种菱形继承的情况:
class A
{
public:
int a;
};
class B : public A
{
public:
int b;
};
class C : public A
{
public:
int c;
};
class D : public B, public C
{
public:
int d;
};

我们可以用 VS 提供的开发人员命令提示工具来显示 D 类型对象的内存布局如下:

- D 对象中包含 B 对象和 C 对象,其中 B 对象和 C 对象又分别包含 A 对象,所以每一个 D 对象包含了两份 A 对象的数据,所以最终每一个 D 对象包含了:B::a, b, C::a, c, d,也就是 5 个 int 值,sizeof(D) = 5 * 4 = 20
- 侧边的数据表示该数据在对象中的内存分布的偏移量
打开 vs 开发人员命令提示符后,首先定位到类所在的文件夹,然后输入:cl [类所在文件名称.cpp] /d1 reportSingleClassLayout[需要显示内存分布的类的名称]。
以显示上面 D class 为例,先 cd 到类所在文件夹,然后输入:cl NormalInheritance.cpp /d1 reportSingleClassLayoutD
回车即可
为了解决以上的在多继承中可能出现的继承多份基类数据成员的问题,C++ 采用的是一种虚继承的方式,指定某一个类为虚基类(virtual base class),然后其直接子类虚继承这个虚基类,那么其所有的子类,无论是直接子类还是间接子类,都只拥有一份虚基类的数据成员:
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;
};
- class A 就是虚基类,class B 和 class C 是它的直接子类,class D 是它的间接子类
我们可以对比前后各个类的内存分布情况:

- 继承虚基类的直接子类都会有一个虚基类表指针(virtual base table pointer, vbptr),指向一个虚表(virtual table),虚表中记录着虚基类数据相对于本类的偏移。比如说,对于 class B, class C,它们的虚表如下:

- class B 中 virtual base A 数据的偏移量为 8,从 B 的内存分布可以验证
- class C 中 virtual base A 数据的偏移量为 8,同理,从 C 的内存分布可以验证
- class D 中就包含 class B 的 vbptr,b, class C 的 vbptr, c, 以及一份 class A 的数据。这两个 vbptr 分别指向两个 vitual table:

- class D 中的 class B 成分中的 class A 数据的偏移量为 20;class D 中的 class C 成分中的 class A 数据的偏移量为 12 (20 - 8)。
本文介绍了C++中的虚继承机制,用于解决多继承可能导致的同一基类数据成员被多次包含的问题。通过虚基类,确保所有子类只拥有基类的一份数据。详细阐述了虚继承的实现原理,包括虚基类表指针(vbptr)和虚表(virtual table),并展示了虚继承前后类的内存分布变化。

279

被折叠的 条评论
为什么被折叠?



