【C++进阶】一文吃透静态绑定、动态绑定与多态底层机制(含虚函数、vptr、thunk、RTTI)

【C++进阶】一文吃透静态绑定、动态绑定与多态底层机制(含虚函数、vptr、thunk、RTTI)

作者:你的C++教练
日期:2025-08-01


目录

  1. 静态绑定 vs 动态绑定
  2. 非虚函数的三大坑
  3. 多态的四要素
  4. 虚析构函数为什么必须写?
  5. 探秘 vptr/vftablethunk
  6. RTTI 与 dynamic_cast 的底层真相
  7. 虚继承下的虚表偏移
  8. 性能、inline 与构造语义
  9. 实战代码与汇编级分析


1️⃣ 静态绑定 vs 动态绑定

绑定类型决定时机典型场景性能
静态绑定 (Static Binding)编译期普通成员函数、缺省实参直接 call,零额外开销
动态绑定 (Dynamic Binding)运行期虚函数通过指针/引用调用一次 vptr 解引用 + 间接 call

一句话:“指针/引用 + 虚函数”才会触发动态绑定,否则全是静态绑定。


2️⃣ 非虚函数的三大坑

① 普通函数静态绑定
struct B { void foo() { puts("B"); } };
struct D : B { void foo() { puts("D"); } };B* p = new D;
p->foo();          // 输出 B!(静态绑定)
② 缺省实参静态绑定
struct B {virtual void f(int x = 1) { cout << x; }
};
struct D : B {void f(int x = 2) override { cout << x; }
};B* p = new D;
p->f();  // 输出 1!缺省值来自 B 的定义
③ 非虚析构函数 → 内存泄漏
B* p = new D;
delete p;   // 只调 ~B(),~D() 不会被调用

3️⃣ 多态的四要素

条件说明
继承存在父子类
虚函数父类至少一个 virtual
重写子类覆盖父类虚函数
指针/引用用父类指针/引用指向子类对象

示例:

class A { public: virtual void vf() { puts("A"); } };
class B : public A { void vf() override { puts("B"); } };A* p = new B;
p->vf();   // 动态绑定,输出 B

4️⃣ 为什么必须写虚析构函数?

Base* p = new Derived;
delete p;  // 只有 ~Base() 是 virtual,才会:
  1. 先通过 vptr 找到 Derived::~Derived
  2. 执行 ~Derived
  3. 自动插入 ~Base()
  4. 最终 operator delete 释放内存

结论:任何可能被继承的类,析构函数都写成 virtual


5️⃣ 探秘 vptr / vftable / thunk

对象模型(简化)
对象地址
├─ vptr ----┐
├─ 成员变量 │
│           │
v           v+--------------+
vftable | &Base::foo   |  <-- 如果未被覆盖+--------------+| &Derived::bar|+--------------+
thunk 是什么?
  • 当用 第二基类指针 指向多重继承的子对象时,需要调整 this 偏移。
  • 编译器生成一段 汇编桩代码(thunk)放在虚表中:
    thunk:sub  this, offset   ; 调整 thisjmp  Derived::foo   ; 真正虚函数
    
  • 虚表项指向的就是 thunk 地址。

6️⃣ RTTI 与 dynamic_cast

if (Derived* d = dynamic_cast<Derived*>(basePtr)) {d->onlyInDerived();
}
实现原理
  • 每个有虚函数的类都会在虚表 -1 位置type_info 指针。
  • dynamic_cast 通过 vptr[-1] 比较 RTTI 信息,决定转换是否成功。

7️⃣ 虚继承下的虚表偏移

struct VBase { virtual void vf(); };
struct A : virtual VBase { void vf() override; };
  • 虚基类子对象在内存中可能位于 对象尾部
  • vptr 需要 间接寻址 才能找到虚基类中的虚函数,带来额外一次指针解引用。

8️⃣ 性能 & inline 提醒

因素开销
虚函数调用一次额外内存读取
多重继承可能增加 thunk
虚继承两次指针解引用
inline 失败递归、过大、地址取址都会阻止

建议:性能关键路径避免深度虚继承,热点函数尽量 final/inline


9️⃣ 构造语义 & 汇编级分析

伪代码回顾
C::C() {B::B();               // 基类构造A::A();           // 再基类vptr = A::vftable;vptr = B::vftable;vptr = C::vftable;    // 最终态
}
  • 构造期间 对象类型不断变化,虚表指针逐级覆盖。
  • 析构期间 反向逐级回退,保证 dynamic_cast/typeid 行为正确。

🔚 结论速记

规则口诀
需要多态“指针引用 + virtual”
析构函数“能继承就 virtual”
缺省实参“静态绑定,别在虚函数里玩默认值”
RTTI“至少一个 virtual 才能 dynamic_cast
性能“虚函数=一次间接寻址,虚继承=两次”

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/91704.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/91704.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

VUE基础知识2

1.计算属性&#xff1a;使用计算属性来描述依赖响应式状态的复杂逻辑。关键字computed:{}//计算属性&#xff0c;使用的时候和函数方法不一样&#xff0c;不需要加括号。简单来说就是模板方法的复杂逻辑放到了计算属性中去。2.计算属性缓存VS方法&#xff1a;计算属性值会基于其…

在PyCharm中将现有Gitee项目重新上传为全新项目

如果你想将当前本地的Gitee项目重新上传为一个全新的Gitee项目&#xff08;保留本地代码但断开与原仓库的关联&#xff09;&#xff0c;可以按照以下步骤操作&#xff1a; 删除旧的Git远程仓库关联 打开PyCharm&#xff0c;进入你的项目 点击顶部菜单 Git > Manage Remotes …

设计模式1:创建型模式

设计模式1&#xff1a;创建型模式 设计模式2&#xff1a;结构型模式&#xff08;编写中&#xff09; 设计模式3&#xff1a;行为型模式&#xff08;编写中&#xff09; 前言 设计模式是软件开发中经过验证的可复用解决方案&#xff0c;它们源自实践、提炼于经验&#xff0c;并…

React--》规划React组件库编码规范与标准 — Button篇

目前前端组件化已经成为前端开发的核心思想之一&#xff0c;在这篇文章中将深入探讨如何规划一个规范的Button组件&#xff0c;让它不仅能高效支持不同的功能需求还能确保跨项目、跨团队的一致性&#xff0c;抛砖引玉的方式引出后面组件库的其他组件的开发&#xff01; 目录 B…

中科米堆CASAIM金属件自动3d测量外观尺寸三维检测解决方案

金属零部件的外观尺寸检测直接关系到产品的装配精度和使用性能。CASAIM基于激光扫描技术的自动化三维扫描系统&#xff0c;为金属加工行业提供了高效的自动3D测量解决方案&#xff0c;有效解决了传统检测方式效率低、覆盖面有限等问题。激光扫描技术在金属件测量中优势明显。与…

开源数据同步中间件,支持MySQL、Oracle

DBSyncer&#xff08;英[dbsɪŋkɜː(r)]&#xff0c;美[dbsɪŋkɜː(r) 简称dbs&#xff09;是一款开源的数据同步中间件&#xff0c;提供MySQL、Oracle、SqlServer、PostgreSQL、Elasticsearch(ES)、Kafka、File、SQL等同步场景。支持上传插件自定义同步转换业务&#xff0…

中英混合的语音识别XPhoneBERT 监督的音频到音素的编码器结合 f0 特征LID

完整项目包获取点击文末名片完成一个 Code-Switching&#xff08;中英混合&#xff09;的语音识别系统&#xff0c;整个流程如下思路进行&#xff1a; 163. (Step 1) 训练音频到音素的编码器&#xff08;Audio → Phoneme Encoder&#xff09; 你已经完成了此部分。核心思路是利…

Param关键字的使用

1&#xff1a;当一个方法的某一个参数个数不固定的时候&#xff0c;可以使用Param2:可变的方法参数必须定义为数组类型3&#xff1a;该参数必须放在方法参数的最后&#xff0c;应且只有一个4&#xff1a;参数必须为一维数组5&#xff1a;params不能和ref和out组合使用namespace…

京东云轻量云服务器与腾讯云域名结合配置网站及申请SSL证书流程详解

京东云轻量云服务器与腾讯云域名结合配置网站及申请SSL证书流程详解 1. 需求及实现效果 1.1. 需求 先说一下我当前情况&#xff0c;我目前有一个京东云服务器和一个在腾讯云旗下买的域名&#xff08;不要问为啥一个在京东云&#xff0c;一个在腾讯云&#xff0c;那自然是哪个…

Python入门Day14:面向对象编程初步(OOP入门)

学习目标&#xff1a;理解面向对象编程&#xff08;OOP&#xff09;的基本思想&#xff1a;类&#xff0c;对象掌握类的定义&#xff0c;构造方法&#xff0c;实例属性和方法熟悉self的含义与作用学会用类组织和封装代码&#xff0c;初步构建自己的“对象世界”一、什么是面向对…

日志和指标标签规范化方案

好的&#xff0c;设计一个有效的日志和指标标签规范化方案对于构建可观测性强、易于维护、关联分析顺畅的系统至关重要。混乱的标签命名会极大增加查询、聚合、告警和故障排除的难度。 以下是一个综合性的标签规范化方案建议&#xff0c;结合了行业最佳实践&#xff1a; 核心目…

Windows和Linux的tree工具

目录 1.前言 2.Linux的tree工具 2.1.安装tree 2.2.常用命令与参数 2.3.常见应用场景 2.4.注意事项 3.Windows的tree工具 3.1.基础语法 3.2.核心参数详解 3.3.常见应用场景 3.4.局限性与增强方案 4.Windows 与 Linux tree 的核心差异 5.tree工具优势 5.总结 相关…

[echarts] 更新数据

option {title: { text: 销售数据 },tooltip: { trigger: axis },legend: { data: [销量, 库存] },xAxis: {type: category,data: [衬衫, 羊毛衫, 雪纺衫]},yAxis: { type: value },series: [{ name: 销量, type: bar, data: [5, 20, 36] },{ name: 库存, type: line, data: […

通过el-image实现点击文字查看图片,及其图片列表

场景一&#xff1a;表格中有时候会有点击文字查看图片的功能&#xff08;因为表格的一个单元格不方便显示多个图片&#xff09;如下图所示&#xff1a;对于这个需求&#xff0c;我们可以应对的方案是&#xff1a;在文字旁边写一个el-image图默认显示多张图片中的第一张&#xf…

003 实习(前端jquery之轮播图,学校网页)

web前端,查询官网:w3schoolHTML:负责网页结构&#xff08;页面元素和内容&#xff09;CSS:负责网页的表现&#xff08;网页元素的外观、位置等页面样式&#xff0c;如颜色&#xff0c;大小&#xff09;JAVAScript:负责网页的行为&#xff08;交互效果&#xff09;<a>:超链…

Mysql group by

临时表与内存表 内存表是 Memory 引擎表&#xff0c;表的数据行都在内存。 临时表可以使用各种引擎。 临时表是线程私有表&#xff0c;其他线程不可见&#xff0c;不需考虑重名问题。 session 结束时临时表会被自动删除。 如果 Binlog_format row&#xff0c;则临时表语句不进…

Linux(15)——进程间通信

目录 一、进程间通信的介绍 ✍️进程间通信的目的 ✍️进程间通信的本质 进程间通信的分类 ✍️管道 ✍️System V IPC ✍️POSIX IPC 二、管道 &#x1f9e0;什么是管道 ✍️匿名管道 &#x1f4dd;匿名管道的原理 &#x1f4dd;pipe函数 &#x1f4dd;匿名管道…

【Flutter】双路视频播放方案

最近在做双路视频播放&#xff0c;就是在一个页面播放两个视频。我遇到的问题就是音频焦点冲突问题&#xff0c;在下面说明。什么是双路视频播放&#xff08;来自AI&#xff09;双路视频播放&#xff08;Dual-Video Playback&#xff09;&#xff0c;从字面上理解&#xff0c;就…

笔试——Day25

文章目录第一题题目思路代码第二题题目&#xff1a;思路代码第三题题目&#xff1a;思路代码第一题 题目 笨小猴 思路 模拟 统计每个字符出现的次数&#xff0c;用最大减最小&#xff0c;判断是不是质数&#xff1b; 质数的判断使用试除法&#xff1b; 代码 第二题 题目&…

【C#学习Day15笔记】拆箱装箱、 Equals与== 、文件读取IO

前言在C#第15天的学习中&#xff0c;我深入探索了类型转换机制、对象比较原理和文件操作技术三大核心主题。这些知识是构建高效、健壮程序的关键基础。本文完整保留我的课堂实践代码和命名体系&#xff0c;通过结构化梳理帮助大家掌握这些核心概念。所有代码示例均来自我的实际…