首页 > 代码库 > 多继承(虚继承)派生类对象内存结构

多继承(虚继承)派生类对象内存结构

在这里谈一下虚继承。前面写过派生类对象的内存结构,都是基于VS2010编译器的,不同的编译器对于继承的处理不同,但本质都是一样的。

虚继承是解决共享基类问题的。例如在菱形继承中


如果不使用虚继承,基类A在D中会有两个,这不仅浪费内存,还会造成歧义。使用虚继承就可以解决基类共享的问题。

要想在派生类中共享基类(例如在D对象中只有一个A对象,这时候D对象中的B对象和C对象都可以查找到A,而不是在B对象和C对象中各含有一个A对象)。

先看下面一个例子:

#include<iostream>
using namespace std;
class A{
public:
	A():a(10){};
	int a;
};
class B: public virtual A{
public:
	B():b(20){};
	int b;
};
class C:public virtual A{
public:
	C():c(30){};
	int c;
};
class D:public B, public C{
public:
	D():d(40){};
	int d;
};
int main()
{
	cout<<"A size is "<<sizeof(A)<<endl;
	cout<<"B size is "<<sizeof(B)<<endl;
	cout<<"C size is "<<sizeof(C)<<endl;
	cout<<"D size is "<<sizeof(D)<<endl;
	return 0;
}
VS2010运行结果如下:

A size is 4
B size is 12
C size is 12
D size is 24

之前在网上看过一些文章,解决虚继承共享对象的一个方案就是在虚函数表中添加共享对象的相对偏移。我们把D类对象各个部分打印出来看一下:

D d;

	int **p=(int **)&d;
	cout<<"以4字节为单位,分成5部分:"<<endl;
	for(int i=0; i<6; i++)
		cout<<"第"<<i<<"个部分的值"<<p[i]<<endl;

以4字节为单位,分成5部分:
第0个部分的值013078B0
第1个部分的值00000014
第2个部分的值013078A4
第3个部分的值0000001E
第4个部分的值00000028
第5个部分的值0000000A

可以看出第0部分和第2部分不是成员变量,其实是虚函数表指针,在虚函数表中记录着第5部分(就是虚基类A对象)相对其他对象的偏移。第0部分记录着相对对象B和相对对象D的偏移(可以看出偏移20个字节),第2部分记录着相对C对象的偏移(偏移12个字节),可以查看一下:

cout<<p[0][1]<<endl;
	cout<<p[2][1]<<endl;
输出的结果就是20和12。

至于为什么在虚函数表的第一个位置,我还没搞懂,不过我想应该是不同的编译器有不同的安排,但是它们的目的都是共享虚基类A对象。

在D中查找虚基类,首先找到虚函数表,然后找到偏移的大小,之后才能找到虚基类对象,可见虚基类降低了效率。

多继承(虚继承)派生类对象内存结构