0

0

c++的虚函数表(vtable)是如何工作的? (深入理解多态)

裘德小鎮的故事

裘德小鎮的故事

发布时间:2026-01-11 12:35:33

|

708人浏览过

|

来源于php中文网

原创

虚函数表指针(vptr)始终位于对象内存起始处,指向编译期生成的虚函数表(vtable);vtable按虚函数声明顺序存储函数指针,构造/析构中vptr动态更新以保障正确多态调用。

c++的虚函数表(vtable)是如何工作的? (深入理解多态)

虚函数表指针(vptr)在对象内存布局中的位置

每个含有虚函数的类,编译器会在其对象的最开始处隐式插入一个 vptr(虚函数表指针),指向该类的虚函数表(vtable)。这个指针大小取决于平台(通常是 8 字节在 64 位系统上),且**永远位于对象内存的起始地址**。

这意味着:sizeof 一个含虚函数的类,一定 ≥ 指针大小;即使类中只有虚函数、无成员变量,sizeof 也不为 0。

  • 派生类对象也包含自己的 vptr,但若未重写基类虚函数,则对应表项仍指向基类实现
  • 多重继承时,子对象可能有多个 vptr(例如每个虚基类子对象一份),布局更复杂,但主流编译器(如 GCC、MSVC)通常只在最派生对象开头放一个主 vptr,其余通过偏移调整
  • 注意:vptr 是编译器自动维护的,无法在 C++ 源码中直接访问或修改;尝试用 reinterpret_cast 强转取址属于未定义行为

虚函数表(vtable)的内容与生成时机

vtable 是编译期生成的静态数组,每个类(而非每个对象)一份,存储的是函数指针(void (*)() 类型),按虚函数声明顺序排列。纯虚函数在表中存为 nullptr 或特定陷阱地址(如 GCC 填 __cxa_pure_virtual)。

关键点:

立即学习C++免费学习笔记(深入)”;

  • 构造函数中,对象的 vptr 会被逐步设置:先设为基类 vtable 地址,进入派生类构造函数后才更新为派生类 vtable
  • 因此,在基类构造函数里调用虚函数,实际执行的是基类版本——哪怕派生类已重写,此时 vptr 还没被改写
  • 析构同理:析构顺序与构造相反,vptr 逐层回退,确保调用对应层级的虚函数

多态调用如何通过 vtable 实现(以 g++ 为例)

当通过基类指针或引用调用虚函数时,CPU 执行流程是:取对象首地址 → 解引用 vptr 得到 vtable 起始地址 → 按虚函数在类中声明顺序计算偏移(如第 0 个是 func1,则取 vtable[0])→ 调用该地址处的函数

DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

下载

示例代码可验证这一机制:

#include 
struct Base {
    virtual void f() { std::cout << "Base::f\n"; }
    virtual void g() { std::cout << "Base::g\n"; }
};
struct Derived : Base {
    void f() override { std::cout << "Derived::f\n"; }
};
int main() {
    Derived d;
    // 强制读取 vptr(仅用于演示,非标准做法)
    void** vptr = *static_cast(&d);
    std::cout << "vtable addr: " << vptr << "\n";
    // 调用 vtable[0](即 f())
    using Func = void(*)();
    Func f_ptr = reinterpret_cast(vptr[0]);
    f_ptr(); // 输出 "Derived::f"
}

注意:reinterpret_cast 访问 vtable 是非便携、非标准行为,仅用于教学观察;实际项目中绝不应依赖此方式。

虚函数调用的性能开销与优化边界

相比普通函数调用,虚函数多一次内存加载(从对象取 vptr)+ 一次间接跳转(查 vtable + call),现代 CPU 的分支预测器通常能很好处理这种规律性跳转,所以开销极小(通常就几个周期)。

但以下情况会破坏可预测性,导致性能下降:

  • 热路径中频繁切换不同子类对象(vtable 地址变化大,vptr 加载后缓存局部性差)
  • 虚函数内联失败:编译器无法在编译期确定目标函数,故不内联(除非启用 LTO + 全局分析)
  • 启用了控制流完整性(CFI)等安全机制时,间接调用需额外验证,开销上升

真正影响性能的往往不是 vtable 查找本身,而是虚函数常伴随动态内存分配、缓存不友好访问模式等副作用。别过早优化 vtable,先确认它真是瓶颈。

相关专题

更多
java多态详细介绍
java多态详细介绍

本专题整合了java多态相关内容,阅读专题下面的文章了解更多详细内容。

15

2025.11.27

javascriptvoid(o)怎么解决
javascriptvoid(o)怎么解决

javascriptvoid(o)的解决办法:1、检查语法错误;2、确保正确的执行环境;3、检查其他代码的冲突;4、使用事件委托;5、使用其他绑定方式;6、检查外部资源等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

175

2023.11.23

java中void的含义
java中void的含义

本专题整合了Java中void的相关内容,阅读专题下面的文章了解更多详细内容。

96

2025.11.27

c++主流开发框架汇总
c++主流开发框架汇总

本专题整合了c++开发框架推荐,阅读专题下面的文章了解更多详细内容。

78

2026.01.09

c++框架学习教程汇总
c++框架学习教程汇总

本专题整合了c++框架学习教程汇总,阅读专题下面的文章了解更多详细内容。

46

2026.01.09

学python好用的网站推荐
学python好用的网站推荐

本专题整合了python学习教程汇总,阅读专题下面的文章了解更多详细内容。

121

2026.01.09

学python网站汇总
学python网站汇总

本专题整合了学python网站汇总,阅读专题下面的文章了解更多详细内容。

11

2026.01.09

python学习网站
python学习网站

本专题整合了python学习相关推荐汇总,阅读专题下面的文章了解更多详细内容。

15

2026.01.09

俄罗斯手机浏览器地址汇总
俄罗斯手机浏览器地址汇总

汇总俄罗斯Yandex手机浏览器官方网址入口,涵盖国际版与俄语版,适配移动端访问,一键直达搜索、地图、新闻等核心服务。

71

2026.01.09

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
CSS3 教程
CSS3 教程

共18课时 | 4.4万人学习

Sass 教程
Sass 教程

共14课时 | 0.8万人学习

Pandas 教程
Pandas 教程

共15课时 | 0.9万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号