Class
虚函数可以继承 可以在子类中被重写
动态绑定
虚函数被调用时会根据实例类型动态决定被调用函数。
虚函数表可以实现动态绑定 每个实例都持有一个vtable,可以根据vtable确定被调用函数。
无继承
虚函数表的指针会放在对象内存开头的一个字长的位置
单继承
- 虚函数表内部按照继承顺序存放虚函数表项
- 子类新定义的虚函数存放到最后
第 1 步:复制 (Copy)
- 动作:完整地复制一份父类
Parent的虚函数表作为蓝本。 - 结果:我们得到一个临时的表,内容和
Parent的 vtable 一模一样。- 索引 0:
&Parent::func1 - 索引 1:
&Parent::func2
- 索引 0:
第 2 步:修改 (Modify / Override)
动作:检查
Child类是否重写了父类的虚函数。结果:
- 编译器发现
Child重写了func1。于是,它将表中索引为 0 的那一项,从&Parent::func1修改为&Child::func1。 - 编译器发现
Child没有动func2,所以索引为 1 的那一项保持不变,仍然是&Parent::func2。
此时,表的更新为:
- 索引 0:
&Child::func1(已修改) - 索引 1:
&Parent::func2(保持不变)
- 编译器发现
第 3 步:追加 (Append)
- 动作:检查
Child类是否定义了任何新的虚函数(父类中没有的)。 - 结果:
- 编译器发现了新的虚函数
func3。于是,它将这个新函数的地址&Child::func3追加到表的末尾。
- 编译器发现了新的虚函数
多继承
一个子类继承了多个父类。
处理方式也很简单 按照声明的顺序以此复制
然后修改、追加。
RTTI机制
Run Time Type Information
运行时获取对象的类型结构信息 继承关系
多态
用一种类型的指针 灵活的取多种class
比如
Animal* cage[] = { new Dog(), new Cat(), new Dog() }; |
实现
由于多态的存在 一个父类指针可能指向各种不同的对象,父类需要随时知道指向的是哪个子类的对象
RTTI解决的核心问题:安全地识别和使用对象的真实类型,而不是它在编译时表现出的类型。
两大核心工具
dynamic_cast
作用: 安全地将父类指针转换为子类指针
原理是运行时检查RTTI信息,如果是想要的类型 才会返回有效的指针。
用法
Dog* p_dog = dynamic_cast<Dog*>(p_animal); |
** typeid **
返回type_info类地常量对象引用
用法
Dog my_dog; |
typeid更多用于比较
识别对象的原理
GCC中type_info其实也有不同的派生类
根据复杂度有如下几种
| 类类型 | 对应的派生类 | 包含的信息 |
|---|---|---|
| 基础类型 (int, float) | __fundamental_type_info |
仅类型名 |
| 无基类的类 | __class_type_info |
仅类型名 |
| 单一公有继承 | __si_class_type_info |
指向基类 type_info 的指针 |
| 多重/虚继承/非公有 | __vmi_class_type_info |
标志位、基类列表、偏移量、属性 |
Object Pointer --> [ vptr ] --+ |
** 核心 **: __vmi_class_type_info
__base_class_type_info 结构
每个基类的信息都封装在这个结构中,它包含两个关键部分:
__base_type: 指向基类type_info的指针。__offset_flags: 这是一个 long 类型,通过位运算存储了位移和属性。
定位起始点:通过 src 对象的 vptr 找到 vtable[-1],获取当前对象的 __vmi_class_type_info。
搜索家谱:
- 如果当前类就是
Dst,根据offset-to-top计算地址并返回。 - 如果是继承关系,它会递归遍历
__vmi_class_type_info里的base_info数组。
计算偏移:
- 它会检查每个基类的
__offset_flags。 - 如果是普通继承,直接加上
offset。 - 如果是虚继承(
__virtual_mask为 1),它会去对象的 vbtable (或在 vtable 里的虚基类偏移区域) 查找动态偏移。
权限校验:检查 __public_mask。如果路径上有 private 继承且你不是在类内部转换,转换会失败返回 nullptr。
stack
现代编译器更青睐于使用rsp
