虚函数表vtable的初始化时机虚函数表的初始化分为两个阶段一个是编译/链接期一个是运行期二者分工不同编译/链接期vtable 结构的确定静态阶段编写包含虚函数的类比如class A { virtual void f(); }并编译时编译器会为每个包含虚函数的类或继承了虚函数的子类生成一份固定的 vtable 数据结构本质是函数指针数组。这个 vtable 会被编译到程序的只读数据段.rodata中和代码段一样是程序启动前就确定好的“静态数据”。此时 vtable 里已经填好了各个虚函数的地址比如A::f的地址。运行期vptr虚函数表指针的初始化动态阶段每个包含虚函数的对象都会有一个隐藏的vptr成员指向所属类的 vtable这个指针的初始化对于普通对象vptr在对象构造函数执行的最开始阶段完成初始化早于构造函数的函数体执行子类对象构造时会先初始化父类的vptr再初始化子类自己的vptr。对于全局/静态对象vptr在程序启动后的全局初始化阶段main 函数执行前完成。对于 new 创建的对象vptr在operator new分配内存后、构造函数执行前初始化。验证代码C#includeiostreamusingnamespacestd;classBase{public:Base(){// 此时 vptr 已经指向 Base 的 vtable调用虚函数会走 Base 版本func();// 输出 Base::func}virtualvoidfunc(){coutBase::funcendl;}};classDerived:publicBase{public:Derived(){// 此时 vptr 已经切换到 Derived 的 vtablefunc();// 输出 Derived::func}voidfunc()override{coutDerived::funcendl;}};intmain(){Derived d;// 构造过程// 1. 初始化 Base 部分 → vptr 指向 Base::vtable → 调用 Base::func// 2. 初始化 Derived 部分 → vptr 切换到 Derived::vtable → 调用 Derived::funcreturn0;}输出结果Base::func Derived::func即vptr在构造函数执行时已经完成初始化且子类构造时会覆盖父类的vptr。常见误区❌ 错误“每次创建对象都会生成新的 vtable”✅ 正确一个类只有一份 vtable编译期确定所有对象的vptr都指向这份 vtable。❌ 错误“vptr 在构造函数体执行后初始化”✅ 正确vptr初始化在构造函数的“初始化列表阶段”编译器自动插入早于函数体执行。❌ 错误“虚函数表在运行时动态生成”✅ 正确vtable 本身是编译期生成的静态数据运行时只初始化vptr指向它。总结虚函数表vtable本身编译/链接期生成存储在只读数据段属于类的静态资源全局唯一。虚函数表指针vptr运行期初始化普通对象在构造函数执行前完成子类构造时会覆盖父类的vptr。核心逻辑vtable 是“静态模板”vptr 是“动态指针”二者结合实现多态。