深度剖析 C++ 析构函数

本贴最后更新于 1966 天前,其中的信息可能已经时移世易

构造函数和析构函数在 C++ 中意味着生命周期的开始和结束,它们的实现原理相同。由于析构函数往往还设置成虚函数,所以这里我重点介绍下 C++ 析构函数的原理和各种场景。
一、析构函数的作用
当对象的生命周期结束时,会自动调用析构函数,以清理一些资源,比如释放内存、关闭文件、关闭数据库连接等等。
二、析构函数调用的时机
(1)基类析构
4624d87b3bb64c79bff4bf5a7924c345.png
我们反汇编下查看上面代码:
526d15332d5b4763becae057a465c347.png
从反汇编中可以看出,在对象离开它的作用域时,编译器自动给我们添加了一个析构函数调用的语句。
那我们使用 new 产生的对象会什么时候调用析构函数呢,这里我们把 fun1 里对象改成动态生成。
void fun1()
{
Base *base = new Base();

cout<<"fun1 over"<<endl;
}
当我们不使用 delete 释放内存时,看反汇编的情况
8e129ee60b03423091ba459db2bacf52.png
此时,没有任何地方调用 Base 的析构函数
当我们使用 delete 释放对象时,
void fun1()
{
Base *base = new Base();

delete base;
cout<<"fun1 over"<<endl;
}
我们反汇编结果:
be6c9b24aa034331b5413629ea13b107.png
这里我们看到析构函数调用了,这是因为当我们使用 delete 删除对象时,编译器会自动在后面添加一条调用析构函数的语句;
从这里我们也可以看书,C++ 中的 new 和 delete 和 c 语言中 malloc 和 free 的唯一区别就是使用 new 和 delete 编译会自动添加一条调用构造
函数和析构函数的语句,其他作用一样,都是释放内存,所以我们在 C++ 中一定要是否 new 和 delete 操作内存,不然就没办法调用构造或
析构函数了
(2)派生类析构
前面分析的是基类的析构函数,那派生类对象析构时自身和基类的析构函数什么时候调用呢,这里我们添加一个派生类:
35acbecef8cf464f9f0f298c245cc40c.png
从上面的分析我们可以知道,编译器会给我们添加一个派生类的析构函数调用,我们分析下派生类的析构函数
68239ef1f5bf4c9684c1698955ac708f.png
我们可以看出,派生类析构函数执行完后会执行基类的构造函数,这说明编译器给派生类析构函数后面都会增加一个直接父类的析构函数调用的语句,
这样就能保证派生类的所有父类的析构函数都会调用。
三、为什么要将基类的析构函数设置为虚函数
这里我们将派生类的对象赋值给基类的引用
e36d199a1d1645d3a9440b66e5194c0f.png
这时候我们反汇编看析构函数的调用情况:
25d812bc5e024a09b7d5464a00d5a725.png
这里我们看到,编译器只给我添加了基类的析构函数调用,我们从上面分析指导,只有派生类的析构函数后面会添加父类的析构函数调用,而父类的析构函数
后面没有子类的析构函数调用,这就造成了派生类的析构函数没有调用,导致一些资源没有正常释放。
当我们把基类的析构函数设置为虚函数时,这时我们反汇编:
a28bd90edece4c5fa0f74308d0bb64d0.png
这个时候我们发现,编译器给我们添加调用的就不是一个具体的析构函数了,那我们调用的到底是哪个析构函数呢?
原来当我们将析构函数定义为虚函数的时候,编译器就会为每个对象添加一个虚表,保存着该对象实例类的虚函数指针(这里 Base 对象实例保存的就是 Base 类对应的析构函数指针,
Child 对象实例保存的就是 Child 类的析构函数指针)。这时编译器就不会静态为我们生成调用那个具体的析构函数,而时在运行的时候查找这种虚表,找到对应的调用地址,因为
我们这里是 Child 对象实例,因此调用的就是 Child 类的析构函数,从而保证派生类和父类的析构函数都会调用。
四、什么时候要将析构函数设为虚函数
将所有类设为虚函数好吗?从上面可以看出,设为虚函数后,类的对象实例会增加一个虚表,占用额外的内存空间,而且调用的时候会查表再调用,对程序的性能影响不好;所以,
在真实的开发环境中,只需将那些实现多态的类(将子类对象指向父类引用)的基类设为虚函数就行了,这样可以避免程序性能变差。

  • 程序员

    程序员是从事程序开发、程序维护的专业人员。

    534 引用 • 3528 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...

推荐标签 标签

  • jsoup

    jsoup 是一款 Java 的 HTML 解析器,可直接解析某个 URL 地址、HTML 文本内容。它提供了一套非常省力的 API,可通过 DOM,CSS 以及类似于 jQuery 的操作方法来取出和操作数据。

    6 引用 • 1 回帖 • 462 关注
  • 职场

    找到自己的位置,萌新烦恼少。

    126 引用 • 1699 回帖 • 3 关注
  • H2

    H2 是一个开源的嵌入式数据库引擎,采用 Java 语言编写,不受平台的限制,同时 H2 提供了一个十分方便的 web 控制台用于操作和管理数据库内容。H2 还提供兼容模式,可以兼容一些主流的数据库,因此采用 H2 作为开发期的数据库非常方便。

    11 引用 • 54 回帖 • 641 关注
  • OpenResty

    OpenResty 是一个基于 NGINX 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。

    17 引用 • 40 关注
  • MyBatis

    MyBatis 本是 Apache 软件基金会 的一个开源项目 iBatis,2010 年这个项目由 Apache 软件基金会迁移到了 google code,并且改名为 MyBatis ,2013 年 11 月再次迁移到了 GitHub。

    170 引用 • 414 回帖 • 426 关注
  • 持续集成

    持续集成(Continuous Integration)是一种软件开发实践,即团队开发成员经常集成他们的工作,通过每个成员每天至少集成一次,也就意味着每天可能会发生多次集成。每次集成都通过自动化的构建(包括编译,发布,自动化测试)来验证,从而尽早地发现集成错误。

    14 引用 • 7 回帖 • 3 关注
  • 架构

    我们平时所说的“架构”主要是指软件架构,这是有关软件整体结构与组件的抽象描述,用于指导软件系统各个方面的设计。另外还有“业务架构”、“网络架构”、“硬件架构”等细分领域。

    140 引用 • 441 回帖 • 1 关注
  • 锤子科技

    锤子科技(Smartisan)成立于 2012 年 5 月,是一家制造移动互联网终端设备的公司,公司的使命是用完美主义的工匠精神,打造用户体验一流的数码消费类产品(智能手机为主),改善人们的生活质量。

    4 引用 • 31 回帖 • 7 关注
  • WebSocket

    WebSocket 是 HTML5 中定义的一种新协议,它实现了浏览器与服务器之间的全双工通信(full-duplex)。

    48 引用 • 206 回帖 • 395 关注
  • Docker

    Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的操作系统上。容器完全使用沙箱机制,几乎没有性能开销,可以很容易地在机器和数据中心中运行。

    476 引用 • 899 回帖
  • Ngui

    Ngui 是一个 GUI 的排版显示引擎和跨平台的 GUI 应用程序开发框架,基于
    Node.js / OpenGL。目标是在此基础上开发 GUI 应用程序可拥有开发 WEB 应用般简单与速度同时兼顾 Native 应用程序的性能与体验。

    7 引用 • 9 回帖 • 345 关注
  • Vim

    Vim 是类 UNIX 系统文本编辑器 Vi 的加强版本,加入了更多特性来帮助编辑源代码。Vim 的部分增强功能包括文件比较(vimdiff)、语法高亮、全面的帮助系统、本地脚本(Vimscript)和便于选择的可视化模式。

    27 引用 • 66 回帖 • 1 关注
  • Bootstrap

    Bootstrap 是 Twitter 推出的一个用于前端开发的开源工具包。它由 Twitter 的设计师 Mark Otto 和 Jacob Thornton 合作开发,是一个 CSS / HTML 框架。

    18 引用 • 33 回帖 • 685 关注
  • 钉钉

    钉钉,专为中国企业打造的免费沟通协同多端平台, 阿里巴巴出品。

    15 引用 • 67 回帖 • 370 关注
  • DevOps

    DevOps(Development 和 Operations 的组合词)是一组过程、方法与系统的统称,用于促进开发(应用程序/软件工程)、技术运营和质量保障(QA)部门之间的沟通、协作与整合。

    40 引用 • 24 回帖 • 1 关注
  • 思源笔记

    思源笔记是一款隐私优先的个人知识管理系统,支持完全离线使用,同时也支持端到端加密同步。

    融合块、大纲和双向链接,重构你的思维。

    18779 引用 • 70216 回帖
  • Dubbo

    Dubbo 是一个分布式服务框架,致力于提供高性能和透明化的 RPC 远程服务调用方案,是 [阿里巴巴] SOA 服务化治理方案的核心框架,每天为 2,000+ 个服务提供 3,000,000,000+ 次访问量支持,并被广泛应用于阿里巴巴集团的各成员站点。

    60 引用 • 82 回帖 • 611 关注
  • CSS

    CSS(Cascading Style Sheet)“层叠样式表”是用于控制网页样式并允许将样式信息与网页内容分离的一种标记性语言。

    180 引用 • 447 回帖
  • 脑图

    脑图又叫思维导图,是表达发散性思维的有效图形思维工具 ,它简单却又很有效,是一种实用性的思维工具。

    21 引用 • 58 回帖
  • Sym

    Sym 是一款用 Java 实现的现代化社区(论坛/BBS/社交网络/博客)系统平台。

    下一代的社区系统,为未来而构建

    523 引用 • 4581 回帖 • 694 关注
  • Hprose

    Hprose 是一款先进的轻量级、跨语言、跨平台、无侵入式、高性能动态远程对象调用引擎库。它不仅简单易用,而且功能强大。你无需专门学习,只需看上几眼,就能用它轻松构建分布式应用系统。

    9 引用 • 17 回帖 • 595 关注
  • Vue.js

    Vue.js(读音 /vju ː/,类似于 view)是一个构建数据驱动的 Web 界面库。Vue.js 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件。

    261 引用 • 662 回帖
  • 招聘

    哪里都缺人,哪里都不缺人。

    189 引用 • 1056 回帖 • 1 关注
  • Openfire

    Openfire 是开源的、基于可拓展通讯和表示协议 (XMPP)、采用 Java 编程语言开发的实时协作服务器。Openfire 的效率很高,单台服务器可支持上万并发用户。

    6 引用 • 7 回帖 • 88 关注
  • C++

    C++ 是在 C 语言的基础上开发的一种通用编程语言,应用广泛。C++ 支持多种编程范式,面向对象编程、泛型编程和过程化编程。

    106 引用 • 152 回帖
  • Quicker

    Quicker 您的指尖工具箱!操作更少,收获更多!

    20 引用 • 74 回帖
  • Firefox

    Mozilla Firefox 中文俗称“火狐”(正式缩写为 Fx 或 fx,非正式缩写为 FF),是一个开源的网页浏览器,使用 Gecko 排版引擎,支持多种操作系统,如 Windows、OSX 及 Linux 等。

    7 引用 • 30 回帖 • 450 关注