首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C++ 虚函数表机制深度解析

     編輯:關於C語言

什么是虚函数表

虚函数表是 C++ 编译器为实现动态绑定(运行时多态)而生成的内部数据结构。每个包含虚函数的类(或继承自含虚函数类的派生类)都拥有一张静态的虚函数表,表中按声明顺序存放虚函数的地址。

内存布局

类对象内存模型:
+----------------+
| vptr (隐藏指针) |  --> 指向该类的虚函数表
+----------------+
| 非静态数据成员  |
+----------------+

虚函数表(存储在只读数据段):
+----------------+
| &Base::vfunc1  |
| &Base::vfunc2  |
| &Base::vfunc3  |
+----------------+

vptr 在对象构造时由编译器自动初始化,指向所属类的虚表。单继承下,派生类对象含一个 vptr;多继承/虚继承下可能含多个 vptr。

虚表构建规则

  1. 按声明顺序:基类虚函数先入表,派生类新增虚函数追加。
  2. 覆盖替换:派生类重写(override)基类虚函数时,表中对应槽位替换为派生类函数地址。
  3. 未重写则继承:派生类未重写的虚函数,表项仍指向基类实现。
  4. 纯虚函数:表项通常指向 __cxa_pure_virtual(运行时调用会终止程序)。
  5. 析构函数:若基类析构为虚,析构函数也进入虚表,保证 delete base_ptr 正确调用派生析构。

调用过程

Base *ptr = new Derived();
ptr->vfunc();  // 编译器生成类似:
// 1. 取 ptr->vptr
// 2. 以固定偏移索引虚表,取函数地址
// 3. 间接跳转调用

这就是“间接调用”带来的微小开销(一次内存读取 + 间接跳转),现代 CPU 分支预测可大幅缓解。

多重继承与虚继承下的虚表

多重继承(非虚)

struct A { virtual void f(); };
struct B { virtual void g(); };
struct C : A, B { virtual void h(); };

// C 对象包含两个 vptr:
// [A 子对象 vptr] -> A 的虚表 + C 覆盖/新增
// [B 子对象 vptr] -> B 的虚表 + C 覆盖/新增
// this 指针调整:B::g 调用时 this 需偏移到 B 子对象起始

编译器在虚表中额外存储 thunk(调整 this 指针的小代码桩)或偏移量。

虚继承

虚基类子对象在派生类中仅有一份,虚表项存储 vbase_offset 指示虚基类相对位置,运行时通过 vptr 间接查找。

RTTI 与虚表

虚表首项(或紧邻)常存放指向 type_info 对象的指针,支持 dynamic_cast、typeid 运行时类型识别。

常见误区

  • 虚函数表是每个对象一份 ✗ —— 是每个类一份,全局静态存储。
  • 普通函数也在虚表中 ✗ —— 只有虚函数。
  • 构造/析构中调用虚函数是多态的 ✗ —— 构造时 vptr 尚指向当前类虚表,派生部分未构造,调用基类版本。
  • 内联虚函数 —— 编译器通常不内联虚调用,但若通过对象实例(非指针/引用)调用,可静态绑定并内联。

调试技巧

  • GDB: p /x *(obj->vptr) 查看虚表地址,p *(void**)obj 取 vptr。
  • Clang: -fno-rtti 可禁用 RTTI 节省空间。
  • 编译器输出虚表布局:gcc -fdump-class-hierarchy、clang -cc1 -emit-llvm -o - 查看 IR。

性能优化建议

  1. 热路径避免虚调用:使用 CRTP(静态多态)、模板、std::variant 访问者模式。
  2. 将虚函数标记 final 允许编译器去虚化(devirtualization)。
  3. 数据导向设计:用类型标签 + 函数指针数组替代虚函数,提升缓存局部性。

结语

虚函数表是 C++ 多态的基石,理解其布局、构建规则、多重/虚继承扩展,有助于排查切片问题、优化性能、实现序列化/插件系统等高级场景。记住:虚表属于类,vptr 属于对象,动态绑定 = vptr → 虚表 → 函数地址 → 间接调用。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved