0

0

C++中的析构函数为什么要写成虚函数?(防止基类指针释放时的内存泄漏)

穿越時空

穿越時空

发布时间:2026-01-09 17:06:46

|

272人浏览过

|

来源于php中文网

原创

基类析构函数不加 virtual 会导致资源泄漏,因为用基类指针 delete 派生类对象时仅调用基类析构,跳过派生类析构逻辑,使堆内存、文件句柄等无法释放;只要存在多态删除可能(如基类被继承或用于智能指针),就必须声明为 virtual,否则引发隐蔽泄漏。

c++中的析构函数为什么要写成虚函数?(防止基类指针释放时的内存泄漏)

为什么基类析构函数不加 virtual 会导致资源泄漏

当用基类指针指向派生类对象,并通过该指针 delete 时,若基类析构函数不是虚函数,C++ 只会调用基类的析构函数,**完全跳过派生类的析构逻辑**。这意味着派生类中申请的堆内存、打开的文件句柄、持有的锁等资源无法被释放。

典型错误现象:

Base* p = new Derived();
delete p;  // 只调用 ~Base(),~Derived() 被静默忽略

  • 派生类中 new 出来的内存不会被 delete
  • std::ofstreamFILE* 不会关闭,可能丢失数据
  • 自定义资源管理(如引用计数、GPU buffer)彻底泄露

什么情况下必须把析构函数声明为 virtual

只要存在「多态删除」的可能,就必须加 virtual。核心判断依据不是“有没有继承”,而是“会不会用基类指针/引用来管理派生类对象的生命周期”。

  • 基类设计初衷是被继承(如接口类、抽象基类)→ 必须加 virtual
  • 基类有至少一个 virtual 函数(除析构外)→ 析构也应为 virtual,否则行为不一致
  • 基类被用于容器或智能指针(如 std::vector<:unique_ptr>>)→ 必须加 virtual
  • 基类仅作工具类、无子类、不通过指针销毁 → 可不加,但加了也没坏处

virtual 有什么代价?

虚析构函数会让类变成多态类型,从而引入虚函数表指针(vptr),每个对象增加一个指针大小的开销(通常 8 字节)。但这只是**对象实例的内存开销**,不影响性能热点

  • 构造/析构本身开销极小:虚调用只在 delete 时发生一次,且现代编译器常能内联
  • 不会影响非虚成员函数调用速度
  • 唯一真实代价是:强制要求所有派生类析构函数也隐式为 virtual(符合预期)

反例:有人为省 8 字节而省略 virtual,结果导致难以追踪的资源泄漏——这远比内存开销严重得多。

堆友
堆友

Alibaba Design打造的设计师全成长周期服务平台,旨在成为设计师的好朋友

下载

正确写法与常见误区

标准写法是声明为 virtual,且推荐显式加上 = default 或空实现,避免意外生成非虚版本。

class Base {
public:
    virtual ~Base() = default;  // ✅ 推荐:简洁、明确、无副作用
    // 或
    // virtual ~Base() {}         // ✅ 也可,但不如 = default 清晰
};
  • ❌ 错误:只在派生类写 virtual ~Derived(),基类没写 → 多态删除仍只调用基类析构
  • ❌ 错误:基类析构写成 virtual void cleanup() 而非析构函数 → 无法自动触发,必须手动调用
  • ✅ 正确:哪怕基类没有资源要清理,也要写 virtual ~Base() = default,为子类留出安全出口

最易被忽略的一点:即使你当前所有派生类都只用对象(Derived d;),只要未来有人把它放进 std::unique_ptr 或传给某个通用销毁函数,没加 virtual 的基类析构就会立刻变成隐患。

相关专题

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

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

15

2025.11.27

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

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

174

2023.11.23

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

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

95

2025.11.27

硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

1008

2023.10.19

PHP接口编写教程
PHP接口编写教程

本专题整合了PHP接口编写教程,阅读专题下面的文章了解更多详细内容。

57

2025.10.17

php8.4实现接口限流的教程
php8.4实现接口限流的教程

PHP8.4本身不内置限流功能,需借助Redis(令牌桶)或Swoole(漏桶)实现;文件锁因I/O瓶颈、无跨机共享、秒级精度等缺陷不适用高并发场景。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

353

2025.12.29

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

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

382

2023.07.18

堆和栈区别
堆和栈区别

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

567

2023.08.10

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

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

3

2026.01.09

热门下载

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

精品课程

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

共18课时 | 4.4万人学习

Sass 教程
Sass 教程

共14课时 | 0.7万人学习

Pandas 教程
Pandas 教程

共15课时 | 0.9万人学习

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

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