虚继承与虚基类的内存布局

1. 多重继承的内存布局

struct A
{
	int a;
};

struct B:public A
{
	int b;
};

struct C:public A
{
	int c;
}

struct D
{
	int d;
}
多重继承的情况, 如果父类有共同的祖父类,则祖父类对象被拷贝了多次。在该例中,B的内存布局为(假设从上往下为地址增加方向)

int A::a

int B: b

则D的内存布局为

int B::A:;a

int B:;b

int A::a

int C::c

int D::d

如果sizeof各个类的大小,很明显

sizeof(A) = 4;
sizeof(B) = 8;
sizeof(C) = 8;
sizeof(D) = 20


如果有这样的语句

D *pd = new D;
B *pa = pd;
C *pb = pd;
A *pa = pd;


我们分别输出pa,pd,pc和pd的地址试试

pb = 0x8650a10
pc = 0x8650a18
pd = 0x8650a10

注意,这里pd和pb是相同的,都指向pd的开始位置,但是pc和pb不同,pc产生了两个4字节的偏移,即指向D中C对象的存储位置

pa呢,很明显是具有二义性的,它既可以指向pb中的a,也可以指向pc中的a,自然就会报错。所以上述语句会出现编译错误。

2. 具有虚函数的多重继承

如果多重继承包含虚函数

struct A
{
	int a;
	virtual void f1() {}
};

struct B: public A
{
	int b;
	virtual void f2() {}
};

struct C: public A
{
	int c; 
	virtual void f3() {}
};

struct D: public B, public C
{
	int d;
	virtual void f4() {}
};
此时,A的内存布局为

A::vfptr

int A::a

其中vfptr为指向虚函数表的指针

那么B的布局自然为

A::vfptr

int A::a

int B::b

D的布局为

B::A::vfptr

int B::A::a

int B::b

C::A::vfptr

int C::A::a

int C::c

int D::d

这时各类的大小为

sizeof(A) = 8;
sizeof(B) = 12;
sizeof(C) = 12;
sizeof(D) = 28;
同样指针的位置为

pb = 0x8650a10
pc = 0x8650a1c
pd = 0x8650a10
pa仍然是二义性的

另外注意f4函数,这时D类自定义的虚函数,那么它放在什么地方呢?应该放在第一虚函数表里,也就是B::A::vfptr指向的虚函数表中


3. 虚继承的内存布局

struct A
{
	int a;
};

struct B:virtual public A
{
	int b;
};

struct C:virtual public A
{
	int c;
}

struct D
{
	int d;
}
如果是虚继承,那么在D中只有一个虚基类A的对象,这时就不会出现二义性的情况了

先看B的内存布局,由于B是虚继承A,因此B中会有一个指向虚表的指针,虚表里面存储有当前位置到虚基类的偏移和到当前类的开始位置处的偏移,例如B的内存布局

B::vptr

int B::b

int A::a

可以看到,a的位置放在了最底部,也就是内存地址最高的地方。vptr指向一个虚表,虚表中存储到虚基类的偏移,即8和到当前类的开始处的偏移,这里是0

因此


如果有

pb = new B;
pa = pb;












评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值