【原创】深入理解虚函数表

本文深入探讨了虚函数表在C++中的作用,解释了如何通过指针获取虚函数表,并阐述了动态绑定与静态绑定的区别。内容包括虚函数的继承特性,以及在基类和派生类中的表现。通过实例展示了非虚函数调用的静态绑定过程,强调了编译器视角下名字类型和值类型的重要性。

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

关键字:虚函数,虚函数表,动态绑定,静态绑定,指针

要点

  1. 但凡一个类里有虚函数,无论多少个,它都会维护一个虚函数表,这个虚函数表由这个类管理,所有实例共用一张
  2. 虚函数表会被放在每个实例的最前面——地址空间的前四/八个字节存放虚函数表的指针,这是为了在发生对象切片的时候虚函数表不会被切掉
  3. 假如一个类 class A 有一个实例 a,如何获得其虚函数表?

以32位机为例,如果是64位机,这里需要把 int 换成 double、long long等八字节类型
&a:取 a 的地址
(int*)(&a):将 a 的地址转为 int 类型的指针
● Q1:* 和 & 从符号角度上看起来可以抵消,为什么不直接 (int) a?
○ 非法类型转换,a 不能转换成 int 类型,但 a 的地址可以转换成 int 类型的指针
(int)(&a):对这个 int 类型的指针取值,拿到 虚函数表的地址/指针
(int*)(int)(&a):将这个虚函数表的地址/指针转换为 int 类型的指针,这就是 虚函数表里第一个函数的指针
(int*)(int)(&a) + i:第 i 个函数的指针

  1. 根据 3,可以验证 1,通过取两个 class A 的实例的虚表地址,可以知道它们共用一张表
  2. 虚函数在基类和派生类间的继承有两种情况:如果 override,就实现多态,如果没改,就像普通函数一样继承
    然而没有被 override 的虚函数并不就是普通函数,它会出现在基类和派生类的虚函数表里,编译器只认得它前面的 virtual 标签,不管它在后面是不是 override 了,都需要查表调用
  3. 基于 5,我们说只有需要重写的函数加 virtual,因为哪怕不重写,带 virtual 多了一个查表的环节,就多了一部分运行时开销

实例

代码部分

在这里插入图片描述

  1. A:A::fucn1(虚)、A::func2(虚)、A::func3(普通函数)
  2. B:B::func1(虚)、A::func2(虚)、A::func3(普通函数)、B::func4(普通函数)
  3. 两次调用 func4 均失败,Why?
    因为编译器从来都不知道 pa 的值的真实类型,它就认为 pa 的值是一个 A*
    说明动态绑定依靠的是对象内部存储的虚函数表指针,在运行时找到正确的函数,而 a 也好 pa 也好,它们的类型早在编译期就已经确定,即动态绑定只是函数层面上的多态,并不是类型层面上的多态

另一种情况?

在这里插入图片描述

问题:

假如在基类和派生类中,各自拥有一个返回值、函数名、参数列表完全相同的函数,但不是 virtual,那么这个时候怎么调用?

答:

没有 virtual,说明这两个函数不会出现在虚函数表中,那就不会呈现出多态,B 中的 func1 会覆盖掉继承过来的同名函数
在调用的时候,就像上面说到的,编译器没有分辨右侧对象类型的能力,它这个名字所代表的类型是什么,它的值的类型就是什么
也就是说第 13 行代码里出现了两次静态绑定:首先是 名字 p 与 名字的类型 A 的静态绑定,然后是 名字 p 的值 与 名字 p 的类型 的静态绑定
换个更好理解的说法,第 13 行赋值的过程中,bObject 发生了派生类到基类的类型转换
当然,在第一段代码的第 16 行,也是这两次静态绑定,虚函数只是实现了函数层面上的多态,它不具备类型多态的能力
虚函数实现动态绑定,并不是它可以理解值真实的类型,而是依靠这个对象最前面那几个字节所存储的那个虚函数表
因此,14 行调用 p 的 func1,编译器认为 p 的值的类型是 A,然后根据 A 的数据结构去解读(数据结构,可以理解为编译器与内存间的一种协议,编译器按照这种协议去解析内存中的值)
由于 A 的 func1 并没有 virtual,OK,那直接执行 class A 里面的 func1

和它类似的,就是顶层const、底层const的一种应用,为什么在对象拷贝时,常量的底层const不能赋值给非常量的底层const

总结

虚函数表是很重要,但是透过这个例子,理解编译器视角下 名字的类型 和 值的类型,是更重要的事情

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值