C++是如何实现多态的

C++通过虚函数表实现多态,当基类指针调用虚函数时,会根据对象中的vfptr找到相应的虚函数表,从而实现动态绑定。多态的必要条件包括继承关系、基类的虚函数和派生类的遮蔽。文章详细解释了虚函数表的工作机制,以及对象内存模型中的vfptr如何指向虚函数表,确保基类指针能调用正确的派生类函数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

C++是如何实现多态的

结论:C++通过虚函数来实现多态的,根本原因是派生类和基类的虚函数表的不同

构成多态的必要条件有如下3点:

  • 存在继承关系
  • 基类存在虚函数,且派生类有相同原型的函数遮蔽它
  • 存在基类类型的指针指向派生类对象,且该指针调用了存在遮蔽关系的虚函数

如下图,就是一个简单的多态的例子:(实验环境:vs2019)
在这里插入图片描述

在这里插入图片描述大猪眉头一皱,觉得事情并不简单。

针对上述实验结果,有个疑点:

  • 基类指针是如何知道要调用派生类的f函数,而不是基类的f函数的呢?

这其中的奥秘,还是要从内存模型开始探索。谈到内存模型,那么我们先探讨最简单的——这个对象有多大吧(或者说这个对象占几个字节的内存吧)。
在这里插入图片描述
如上图所示,A的大小在32位编译模式下是4字节,在64位编译模式下是8字节,由此可见,A内存模型里面肯定有个指针!(原因:https://blog.youkuaiyun.com/qq_18138105/article/details/105209406

那么继续深入分析对象的内存模型,这个指针到底指向什么呢?

实际上,这个指针指向一个数组,数组中的每个元素都是虚函数的入口地址,这个数组也就是虚函数表存在于C++内存模型中的常量区)!由于虚函数表和对象在内存上是分开存储的(虚函数在C++内存模型中的代码区,对象在C++内存模型中的堆区,指向对象的指针在C++内存模型的栈区),因此,就需要在对象中需要安插一个指向这个虚函数表的指针!

我们再看一个例子:
在这里插入图片描述
各个对象的内存模型如下(我用cocos creator画的):
在这里插入图片描述
如上图所示,对象内存和虚函数表内存分开的,对象的*vfptr指向对应的虚函数表。(注意:和很多博客画的图不同,为了直观!我是把处于内存高地址的放上面,处于内存低地址的放下面)

仔细观察派生类B的虚函数表

  • 派生类如果存在对基类有遮蔽关系的虚函数,则在虚函数表中则取派生类的这个虚函数的入口地址,如&B::f
  • 对于未被派生类遮蔽的基类的虚函数,派生类的虚函数表则取基类的这个虚函数的入口地址,如&A::g
  • 派生类新增的虚函数,依次往虚函数表后面加,如&B::h

因此,我们通过指针调用虚函数时,先根据指针找到对象里的vfptr来定位到虚函数表,然后通过虚函数在虚函数表中的索引值来得到虚函数的入口地址。

比如 a->f(), 实际上编译器会这么处理 (*(*(a+0)+0))(a)

  • a+0 是 a对象 vfptr 的地址
  • *(a+0)是 vfptr的值, 又 vfptr 是指向虚函数表的指针,因此 *(a+0) 也是虚函数表的首地址
  • 由于 A::f 函数在虚函数表中的索引是0,因此 (*(a+0)+0)就是获取 A::f 函数的入口地址
  • 知道了
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值