虚基类指针vbptr和虚函数指针vfptr

一、虚继承

1.问题:多重继承 ==》 派生类对象中内存重复 ==》 访问冲突 内存浪费
2.解决:对出现多份的数据在最近的继承前加viture
3.虚继承时,虚基类指针vbptr指向虚基类表vbtable,虚基类表中存放的就是数据相对于虚基类指针的偏移,从而根据偏移找到数据
vbptr ==> vbtable
vbtable 中:
0 当前最近作用域的偏移 - vbptr的偏移
1 虚继类数据的起始偏移 - vbptr的偏移

二、不同继承下的内存布局 以及构造顺序

虚基类构造优先级大于普通基类
内存布局:先非虚基类,后 虚基类
1.

  class A
    {
    public:
    	A(int a = 0) :ma(a)
    	{ std::cout << "A" << std::endl; }
    public:
    	int ma;
    };
    class B: virtual public A
    {
    public:
    	B(int b) :A(b), mb(b){ std::cout << "B" << std::endl; }
    public:
    	int mb;
    };
    class C : virtua  public A
    {
    public:
    	C(int c) :A(c), mc(c){ std::cout << "C" << std::endl; }
    public:
    	int mc;
    };
  class D : public B, public C
    {
    public:
    	D(int d) :B(d), C(d),E(d),md(d)
    	{ std::cout << "D" << std::endl; }
    public:
    	int md;
    };
    当A为虚基类,虚继承于b.c时:

在这里插入图片描述
若为多继承时,内存布局如下:
在这里插入图片描述
虚继承的内存布局如下:
在这里插入图片描述

class A
{
public:
	A(int a = 0) :ma(a){ std::cout << "A" << std::endl; }
public:
	int ma;
};
class B: virtual public A
{
public:
	B(int b) :A(b), mb(b){ std::cout << "B" << std::endl; }
public:
	int mb;
};
class C :  viture public A
{
public:
	C(int c) :A(c), mc(c){ std::cout << "C" << std::endl; }
public:
	int mc;
};
class E
{
public:
	E(int e) :me(e){ std::cout << "E" << std::endl; }
public:
	int me;
};
class D : public B, public C, viture public E
{
public:
	D(int d) :B(d), C(d),E(d),md(d){ std::cout << "D" << std::endl; }
public:
	int md;
};

当A为虚基类,虚继承于b.c时且E虚继承D:
在这里插入图片描述
若为多继承时,内存布局如下:
在这里插入图片描述
虚继承的内存布局如下:
第一个vbptr 存放3个数据,因为指针合并,向距离作用域最近的指针内层合并
在这里插入图片描述

class A
{
public:
	A(int a = 0) :ma(a){ std::cout << "A" << std::endl; }
public:
	int ma;
};
class B: virtual public A
{
public:
	B(int b) :A(b), mb(b){ std::cout << "B" << std::endl; }
public:
	int mb;
};
class C :  public A
{
public:
	C(int c) :A(c), mc(c){ std::cout << "C" << std::endl; }
public:
	int mc;
};
class E
{
public:
	E(int e) :me(e){ std::cout << "E" << std::endl; }
public:
	int me;
};
class D : viture public B, public C, public E
{
public:
	D(int d) :B(d), C(d),E(d),md(d){ std::cout << "D" << std::endl; }
public:
	int md;
};

当A为虚基类,虚继承于b时且B虚继承D
在这里插入图片描述
若为多继承时,内存布局如下:
在这里插入图片描述
虚继承的内存布局如下:
第一个vbptr 存放3个数据,因为他代替的是B这个虚基类,c中的A没有vbptr,因为C没有虚继承A,只有B虚继承A,所以只有B的A可被替换成vbptr
在这里插入图片描述
构造顺序:
先是非虚基类,后虚基类
A->C->A->B->E->D
先构造C,C里有基类A,先构造基类A,后C,同理A,B,最后E.D
析构顺序与构造顺序相反

三、虚函数

编译期间,系统会添加一个隐藏成员vfptr
基类指针引用指向或者引用派生类对象时,一定要将基类的析构写成虚函数
1.虚函数和虚继承的对比

class Base
{
public:
	Base(int b) :mb(b){}
	void Show(){}
public:
	int mb;
};
class Derive : virtual public Base
{
public:
	Derive(int d) :md(d), Base(d){}
	virtual void Show(){}
public:
	int md;
};

多继承内存布局:

在这里插入图片描述
虚继承内存布局:
在这里插入图片描述
2、成为虚函数的条件:
1.要能取地址
2.依赖对象调用

构造函数 不可以 系统调用 不依赖对象调用
析构函数 可以
内联函数 不可以 不能取地址 函数在调用点直接展开
static函数 不可以 无this指针 不依赖对象调用

3、虚函数表:
第一行:RTTL(run time type information)运行时类型信息
第二行:虚函数指针的偏移
第三行:虚函数的入口地址
虚表的写入时机 构造函数的执行之前

  • 25
    点赞
  • 67
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值