(巨详细 + 图解) C++多态的机制原理

上一篇我们简单认识了C++的多态, 这次我们来看看多态的底层机制和原理. 不多哔哔, 直接开始.



虚函数表(虚表)和虚表指针

1. 虚表和虚表指针的认识

首先我们来看一个常见的问题, 下面类的对象占几个字节

class Size {
   
public:
	virtual void func1() {
   
		cout << "func1()" << endl;
	}

private:
	int _a = 0;
};

按原来的知识点来说, 成员函数是不体现在对象中的, 所以一个Size对象是4字节

下面进行测试

void Test() {
   
	cout << sizeof(Size) << endl;
}

在这里插入图片描述
我们可以看到结果是8, 显然不是上述的情况, Size类的对象模型如下

在这里插入图片描述

我们可以看到, 除了_a以外, 还有一个_vfptr

这个_vfptr我们叫做虚表指针 (v代表virtual,f代表function)
一个含有虚函数的类中都至少都有一个虚函数表指针,因为虚函数的地址要被放到虚函数表中,虚函数表也简称虚表

我们要明确一下概念

  • 虚函数本身不占用对象空间
  • 使用虚函数后, 对象中会有一个虚表指针
  • 虚表: 存放虚函数的地址 (是一个存放虚函数指针的数组)
  • 虚表指针: 指向虚表的首地址 (本身是一个二级指针)
  • 普通的成员函数地址不会放在虚表中

2. 其他值得注意的问题

下面我们先看一段代码

class Base0 {
   
public:
	virtual void func1() {
   
		cout << "base::func1()" << endl;
	}

	virtual void func2() {
   
		cout << "base::func2()" << endl;
	}

	//普通成员函数地址不会存放在虚表中
	void func3() {
   
		cout << "base::func2()" << endl;
	}

private:
	int _a = 1;
};

class Derive1 : public Base0 {
   
public:
	//重写func1
	virtual void func1() {
   
		cout << "derive::func1()" << endl;
	}
	
	//子类新增的虚函数
	virtual void func4() {
   
		cout << "derive::func4()" << endl;
	}

private:
	int _d = 0;
};

void Test2() {
   
	Base0 b;
	Derive1 d;
}

下面给出对象b和d的内存模型

在这里插入图片描述

1. 我们可以看到, 父类有一张虚表, 子类也继承下来一张虚表, 但是两张虚表的地址是不同的(看vfptr的值即可)
2. 明显可以看到, 子类重写了父类的func1函数, 在虚表中存的是子类的func1函数, 我们把这这个叫做虚函数的重写, 也叫覆盖
3. 虚函数表本质是一个存虚函数指针的指针数组,这个数组以空指针nullptr结尾

总结一下派生类的虚表生成 :

  • a.先将基类中的虚表内容拷贝一份到派生类虚表中
  • b.如果派生类重写了基类中某个虚函数,用派生类自己的虚函数覆盖虚表中基类的虚函数
  • c.派生类自己新增加的虚函数按其在派生类中的声明次序增加到派生类虚表的最后。(vs的调试器不可见, 后面我们会手动调用虚函数以验证)

3. 验证虚表存在哪里

下面我们通过一段代码验证在VS中虚表大致存在哪里, 只是大概的验证, 并不能精确的找到

void tes
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

殇&璃

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值