0

0

c++的虚析构函数(virtual destructor)为什么如此重要? (防止资源泄漏)

穿越時空

穿越時空

发布时间:2026-01-16 12:05:02

|

126人浏览过

|

来源于php中文网

原创

虚析构函数必须声明为virtual,否则通过基类指针delete子类对象时子类析构函数不会被调用,导致资源泄漏;只要存在多态删除场景(如工厂返回指针、容器存储基类指针等),就必须加;基类声明virtual析构后,派生类析构自动为虚,建议显式加override。

c++的虚析构函数(virtual destructor)为什么如此重要? (防止资源泄漏)

虚析构函数不加,delete 父类指针时子类析构函数根本不会调用

这是最直接的后果。当用 Base* 指向一个 Derived 对象,且 Base::~Base() 不是 virtual 时,delete ptr 只会调用 Base::~Base()Derived::~Derived() 完全被跳过。如果子类在析构里释放文件句柄、内存、网络连接或调用 close()/delete,这些操作就彻底漏掉了。

常见错误现象:

  • 程序运行时不报错,但反复创建/销毁对象后出现句柄耗尽(Too many open files
  • Valgrind 报告“still reachable”内存块,实际是子类分配的堆内存没被 delete
  • 资源泄漏只在子类有非 trivial 析构逻辑时才暴露,基类空析构会让问题潜伏很久

什么时候必须声明为 virtual?看多态删除是否可能发生

不是“只要用了继承就要加”,而是“只要存在通过基类指针/引用创建子类对象,并可能用基类指针 delete 它”的场景,就必须加。典型包括:

  • 工厂函数返回 std::unique_ptr 或裸指针
  • 容器存的是 std::vector<:unique_ptr>>
  • 回调系统中注册了子类对象,由框架统一销毁

反例:子类只用于上对象(Derived d;),或完全不通过基类指针销毁,则虚析构不是必需的——但加了也没坏处,且容易误判使用方式,所以只要类设计为被继承,就应默认加 virtual

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

virtual 析构函数对性能和 ABI 的影响其实极小

有人担心虚函数表开销或调用成本,但现实是:

快剪辑
快剪辑

国内⼀体化视频⽣产平台

下载
  • 析构函数本身只调用一次,且通常在对象生命周期末尾,性能敏感度远低于频繁调用的成员函数
  • 编译器能优化掉部分虚调用(如静态类型已知时),但无法保证所有场景
  • ABI 影响仅限于增加一个虚函数表项,不影响对象布局(析构函数不改变 sizeof
  • 现代 C++ 中,若基类已有其他虚函数(比如 virtual void foo() = 0;),加 virtual 析构几乎零成本

真正代价是没加带来的不确定性——你永远不知道下游用户会不会拿你的类做多态删除。

正确写法:基类析构声明为 virtual,子类无需显式加 virtual

只要基类析构是 virtual,派生类的析构自动成为虚函数,即使不写 virtual 关键字。但建议显式写出,提高可读性:

class Base {
public:
    virtual ~Base() = default; // ✅ 推荐:= default 更清晰
};

class Derived : public Base { public: virtual ~Derived() override { / 清理子类资源 / } // ✅ 显式 + override };

注意:= default{} 都可以,但避免写成 ~Base() {} —— 这会抑制编译器生成移动操作,且语义不如 = default 明确。

最容易被忽略的一点:纯虚析构函数必须提供定义,哪怕为空:

class Interface {
public:
    virtual ~Interface() = 0; // 声明纯虚
};
Interface::~Interface() = default; // ✅ 必须定义,否则链接失败

虚析构函数不是“最佳实践”的点缀,而是多态对象生命周期管理的契约底线——漏掉它,等于把资源释放的控制权交给了未定义行为。

相关专题

更多
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的相关内容,阅读专题下面的文章了解更多详细内容。

97

2025.11.27

堆和栈的区别
堆和栈的区别

堆和栈的区别:1、内存分配方式不同;2、大小不同;3、数据访问方式不同;4、数据的生命周期。本专题为大家提供堆和栈的区别的相关的文章、下载、课程内容,供大家免费下载体验。

389

2023.07.18

堆和栈区别
堆和栈区别

堆(Heap)和栈(Stack)是计算机中两种常见的内存分配机制。它们在内存管理的方式、分配方式以及使用场景上有很大的区别。本文将详细介绍堆和栈的特点、区别以及各自的使用场景。php中文网给大家带来了相关的教程以及文章欢迎大家前来学习阅读。

572

2023.08.10

堆和栈的区别
堆和栈的区别

堆和栈的区别:1、内存分配方式不同;2、大小不同;3、数据访问方式不同;4、数据的生命周期。本专题为大家提供堆和栈的区别的相关的文章、下载、课程内容,供大家免费下载体验。

389

2023.07.18

堆和栈区别
堆和栈区别

堆(Heap)和栈(Stack)是计算机中两种常见的内存分配机制。它们在内存管理的方式、分配方式以及使用场景上有很大的区别。本文将详细介绍堆和栈的特点、区别以及各自的使用场景。php中文网给大家带来了相关的教程以及文章欢迎大家前来学习阅读。

572

2023.08.10

数据库Delete用法
数据库Delete用法

数据库Delete用法:1、删除单条记录;2、删除多条记录;3、删除所有记录;4、删除特定条件的记录。更多关于数据库Delete的内容,大家可以访问下面的文章。

269

2023.11.13

C++ 单元测试与代码质量保障
C++ 单元测试与代码质量保障

本专题系统讲解 C++ 在单元测试与代码质量保障方面的实战方法,包括测试驱动开发理念、Google Test/Google Mock 的使用、测试用例设计、边界条件验证、持续集成中的自动化测试流程,以及常见代码质量问题的发现与修复。通过工程化示例,帮助开发者建立 可测试、可维护、高质量的 C++ 项目体系。

8

2026.01.16

热门下载

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

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
国外Web开发全栈课程全集
国外Web开发全栈课程全集

共12课时 | 1.0万人学习

进程与SOCKET
进程与SOCKET

共6课时 | 0.3万人学习

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

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