C++虚继承

参考资料:https://blog.csdn.net/haoel/article/details/3081385

为什么引入虚继承?

#include <iostream>

class B
{
public:
	B() : _ib(10), _cb('B') {}
	virtual void f() { std::cout << "B::f()" << std::endl; }
	virtual void Bf() { std::cout << "B::Bf()" << std::endl; }
	int _ib;
	char _cb;
};

class B1 : public B
{
public:
	B1() : _ib1(100), _cb1('1') {}
	virtual void f() { std::cout << "B1::f()" << std::endl; }
	virtual void f1() { std::cout << "B1::f1()" << std::endl; }
	virtual void Bf1() { std::cout << "B1::Bf1()" << std::endl; }
	int _ib1;
	char _cb1;
};

class B2 : public B
{
public:
	B2() : _ib2(1000), _cb2('2') {}
	virtual void f() { std::cout << "B2::f()" << std::endl; }
	virtual void f2() { std::cout << "B2::f2()" << std::endl; }
	virtual void Bf2() { std::cout << "B2::Bf2()" << std::endl; }
	int _ib2;
	char _cb2;
};

class D : public B1, public B2
{
public:
	D() : _id(10000), _cd('3') {}
	virtual void f() { std::cout << "D::f()" << std::endl; }
	virtual void f1() { std::cout << "D::f1()" << std::endl; }
	virtual void f2() { std::cout << "D::f2()" << std::endl; }
	virtual void Df() { std::cout << "D::Df()" << std::endl; }
	int _id;
	char _cd;
};

int main(void)
{
	D d;
	// std::cout << d._ib << std::endl;  二义性错误错误
	std::cout << d.B1::_ib << std::endl;
	return 0;
}

1>class D size(48):
1> ±–
1> 0 | ±-- (base class B1)
1> 0 | | ±-- (base class B)
1> 0 | | | {vfptr}
1> 4 | | | _ib
1> 8 | | | _cb
1> | | | (size=3)
1> | | ±–
1>12 | | _ib1
1>16 | | _cb1
1> | | (size=3)
1> | ±–
1>20 | ±-- (base class B2)
1>20 | | ±-- (base class B)
1>20 | | | {vfptr}
1>24 | | | _ib
1>28 | | | _cb
1> | | | (size=3)
1> | | ±–
1>32 | | _ib2
1>36 | | _cb2
1> | | (size=3)
1> | ±–
1>40 | _id
1>44 | _cd
1> | (size=3)
1> ±–
1>D::KaTeX parse error: Expected 'EOF', got '&' at position 19: …able@B1@: 1> | &̲D_meta 1> | 0 …vftable@B2@:
1> | -20
1> 0 | &thunk: this-=20; goto D::f
1> 1 | &B::Bf
1> 2 | &D::f2
1> 3 | &B2::Bf2

上面这个例子可以看到多重继承时,在D的实例中B的成员变量_ib和_cb存在两份,为了消除这种场景下的重复数据引入了虚继承。

将上面的例子改为虚继承(B1 B2虚继承至B)然后再看D的内存分布
1>class D size(56):
1> ±–
1> 0 | ±-- (base class B1)
1> 0 | | {vfptr}
1> 4 | | {vbptr}
1> 8 | | _ib1
1>12 | | _cb1
1> | | (size=3)
1> | ±–
1>16 | ±-- (base class B2)
1>16 | | {vfptr}
1>20 | | {vbptr}
1>24 | | _ib2
1>28 | | _cb2
1> | | (size=3)
1> | ±–
1>32 | _id
1>36 | _cd
1> | (size=3)
1> ±–
1>40 | (vtordisp for vbase B)
1> ±-- (virtual base B)
1>44 | {vfptr}
1>48 | _ib
1>52 | _cb
1> | (size=3)
1> ±–
1>D::KaTeX parse error: Expected 'EOF', got '&' at position 19: …able@B1@: 1> | &̲D_meta 1> | 0 …vftable@B2@:
1> | -16
1> 0 | &D::f2
1> 1 | &B2::Bf2
1>D:: v b t a b l e @ B 1 @ : 1 > 0 ∣ − 41 > 1 ∣ 40 ( D d ( B 1 + 4 ) B ) 1 > D : : vbtable@B1@: 1> 0 | -4 1> 1 | 40 (Dd(B1+4)B) 1>D:: vbtable@B1@:1>041>140(Dd(B1+4)B)1>D::vbtable@B2@:
1> 0 | -4
1> 1 | 24 (Dd(B2+4)B)
1>D::$vftable@B@:
1> | -44
1> 0 | &(vtordisp) D::f
1> 1 | &B::Bf

虚继承内存布局特点

看下上例中加了虚继承之后的class B1
1>class B1 size(32):
1> ±–
1> 0 | {vfptr} // B1 新增了两个虚函数f1和Bf1,因此新增了一个自己的虚函数指针
1> 4 | {vbptr} // 虚基指针放在基类B的前面
1> 8 | _ib1
1>12 | _cb1
1> | (size=3)
1> ±–
1>16 | (vtordisp for vbase B)
1> ±-- (virtual base B)
1>20 | {vfptr}
1>24 | _ib
1>28 | _cb
1> | (size=3)
1> ±–
1>B1::KaTeX parse error: Expected 'EOF', got '&' at position 19: …able@B1@: 1> | &̲B1_meta 1> | 0…vbtable@:
1> 0 | -4 // 表示vbptr与B1对象首地址的编译,vbptr前面有个四字节的vfptr
1> 1 | 16 (B1d(B1+4)B) // 基类B相对于虚基指针的偏移 -20-(-4)
1>B1::$vftable@B@:
1> | -20
1> 0 | &(vtordisp) B1::f
1> 1 | &B::Bf

总结:1 虚继承中虚继承的虚基类位于派生类对象的末尾,(虚基指针,虚函数指针,派生类自己成员在基类对象前面,基类对象按声明顺序分布)
2 对于派生类新增的虚函数,派生类对象会产生一个虚函数指针(不同于普通继承会添加到继承的第一个基类的虚函数表中)
3 虚基表中表的第一项表示派生类对象指针相对于虚基指针的偏移,从第二项开始表示各个基类地址相对于虚基指针的偏移

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值