虚函数表vtable与动态派发基础概念C 实现多态的底层机制虚函数表是一个存放虚函数指针的数组每个含有虚函数的类都会有一张虚函数表。虚指针 vptrvirtual pointer对象里隐藏的指针成员指向这个类对应的虚函数表。对象布局 编译器为每个多态类含虚函数的类生成一张静态只读的虚函数表vtbl按声明序存放各虚函数的函数指针槽位 slot每个对象实例头部内嵌一个 vptr 指向其动态类型的vtable。调用路径 p-f() 被编译为两级间接寻址——解引用 vptr 取得 vtable再按编译期确定的槽位索引取出函数指针间接调用即 晚期绑定late binding/ 动态派发dynamic dispatch。覆写语义 派生类 override 在其 vtable 的 同槽位 覆写条目未覆写槽位沿用基类多重继承下一个对象可携带多组 vptr/vtable。工程要点 间接调用阻断内联编译器会尝试 去虚化devirtualization RTTI的 type_info通常也挂载于 vtable。上图展示了派发链Base* p静态类型指向 Derived 对象调用经对象内 vptr 寻址到 Derived vtable同槽位命中被覆写的Derived::f 。代码示例#include iostream struct Base { virtual void f() { std::cout Base::f\n; } // 槽位 [0] virtual void g() { std::cout Base::g\n; } // 槽位 [1] virtual ~Base() default; // 基类析构必须为 virtual }; struct Derived : Base { void f() override { std::cout Derived::f\n; } // 覆写槽位 [0][1] 沿用 Base }; int main() { Base* p new Derived; // 静态类型 Base*动态类型 Derived p-f(); // 经 vptr 寻址 Derived vtable 槽位 [0] → Derived::f p-g(); // 槽位 [1] 未覆写 → Base::g delete p; // 经虚析构正确调用 ~Derived → ~Base std::cout sizeof(Base) \n; // 8x64一个 vptr无数据成员 // 验证同槽位覆写两个 vtable 首槽位偏移一致均为索引 0 }RAIIResource Acquisition Is Initialization资源获取即初始化基础概念资源的获取放在构造函数资源释放放在析构函数。依靠对象生命周期自动管理资源避免内存泄漏、忘记关闭句柄。核心语义 将资源生命周期绑定到对象生命周期——构造函数**获取acquire 资源析构函数 释放release**资源所有权与栈上对象的自动存储期绑定。保障来源 C 的 确定性析构deterministic destruction 语义保证无论是正常返回离开词法作用域还是异常引发 栈展开stack unwinding 已完全构造的自动对象的析构函数必然执行——这是**异常安全exception safetybasic/strong guarantee**的基石。与 GC 对比 释放时机确定、无 finalizer 式的延迟回收典型载体如ock_guard/unique_lock 、fstream、jthread及智能指针本身。代码示例templatetypename T class MyRAII { private: T* ptr; public: explicit MyRAII(T* p) : ptr(p) {} // 获取资源 ~MyRAII() { delete ptr; } // 释放资源 // 禁止拷贝防止两个对象管理同一块内存二次释放 MyRAII(const MyRAII) delete; MyRAII operator(const MyRAII) delete; T operator*() const { return *ptr; } T* operator-() const { return ptr; } }; void test() { MyRAIIint p(new int(10)); // 不管函数正常返回还是抛出异常离开作用域自动调用析构 delete }示例图标准库RAII指针std::unique_ptr独占所有权禁止拷贝只能移动std::movestd::shared_ptr共享所有权引用计数多个 shared_ptr 可以指向同一资源以std::shared_ptr的代码为示例#include iostream #include memory struct Widget { ~Widget() { std::cout ~Widget\n; } }; int main() { auto p1 std::make_sharedWidget(); // 单次分配对象 控制块 std::cout p1.use_count() \n; // 1 { auto p2 p1; // 拷贝构造控制块内 strong count 原子递增 std::cout p1.use_count() \n; // 2 } // p2 析构strong 递减至 1对象存活 std::cout p1.use_count() \n; // 1 } // p1 析构strong 归零 → ~Widget // weak 也归零 → 释放控制块 // 循环引用反例strong 永不归零 → 内存泄漏 // struct Node { std::shared_ptrNode next; }; // 成环泄漏 // struct Node { std::weak_ptrNode next; }; // 用 weak_ptr 打破环路以上便是本篇文章的全部内容如果能帮到您的话欢迎点赞收藏也可以在评论区留下你的想法与疑问。
阅读完成 · 觉得有帮助?