深度剖析 C++ 析构函数

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

构造函数和析构函数在 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 类的析构函数,从而保证派生类和父类的析构函数都会调用。
四、什么时候要将析构函数设为虚函数
将所有类设为虚函数好吗?从上面可以看出,设为虚函数后,类的对象实例会增加一个虚表,占用额外的内存空间,而且调用的时候会查表再调用,对程序的性能影响不好;所以,
在真实的开发环境中,只需将那些实现多态的类(将子类对象指向父类引用)的基类设为虚函数就行了,这样可以避免程序性能变差。

  • 程序员

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

    546 引用 • 3531 回帖 • 1 关注

相关帖子

欢迎来到这里!

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

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

推荐标签 标签

  • DevOps

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

    45 引用 • 25 回帖 • 2 关注
  • 心情

    心是产生任何想法的源泉,心本体会陷入到对自己本体不能理解的状态中,因为心能产生任何想法,不能分出对错,不能分出自己。

    59 引用 • 369 回帖 • 1 关注
  • 钉钉

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

    15 引用 • 67 回帖 • 352 关注
  • Caddy

    Caddy 是一款默认自动启用 HTTPS 的 HTTP/2 Web 服务器。

    11 引用 • 54 回帖 • 146 关注
  • JWT

    JWT(JSON Web Token)是一种用于双方之间传递信息的简洁的、安全的表述性声明规范。JWT 作为一个开放的标准(RFC 7519),定义了一种简洁的,自包含的方法用于通信双方之间以 JSON 的形式安全的传递信息。

    20 引用 • 15 回帖 • 19 关注
  • MyBatis

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

    170 引用 • 414 回帖 • 401 关注
  • 博客

    记录并分享人生的经历。

    272 引用 • 2386 回帖
  • JRebel

    JRebel 是一款 Java 虚拟机插件,它使得 Java 程序员能在不进行重部署的情况下,即时看到代码的改变对一个应用程序带来的影响。

    26 引用 • 78 回帖 • 641 关注
  • 开源中国

    开源中国是目前中国最大的开源技术社区。传播开源的理念,推广开源项目,为 IT 开发者提供了一个发现、使用、并交流开源技术的平台。目前开源中国社区已收录超过两万款开源软件。

    7 引用 • 86 回帖
  • 人工智能

    人工智能(Artificial Intelligence)是研究、开发用于模拟、延伸和扩展人的智能的理论、方法、技术及应用系统的一门技术科学。

    114 引用 • 170 回帖
  • 996
    13 引用 • 200 回帖 • 3 关注
  • App

    App(应用程序,Application 的缩写)一般指手机软件。

    90 引用 • 383 回帖
  • 阿里云

    阿里云是阿里巴巴集团旗下公司,是全球领先的云计算及人工智能科技公司。提供云服务器、云数据库、云安全等云计算服务,以及大数据、人工智能服务、精准定制基于场景的行业解决方案。

    89 引用 • 345 回帖
  • Logseq

    Logseq 是一个隐私优先、开源的知识库工具。

    Logseq is a joyful, open-source outliner that works on top of local plain-text Markdown and Org-mode files. Use it to write, organize and share your thoughts, keep your to-do list, and build your own digital garden.

    5 引用 • 62 回帖 • 9 关注
  • 服务

    提供一个服务绝不仅仅是简单的把硬件和软件累加在一起,它包括了服务的可靠性、服务的标准化、以及对服务的监控、维护、技术支持等。

    41 引用 • 24 回帖 • 2 关注
  • Flume

    Flume 是一套分布式的、可靠的,可用于有效地收集、聚合和搬运大量日志数据的服务架构。

    9 引用 • 6 回帖 • 616 关注
  • 友情链接

    确认过眼神后的灵魂连接,站在链在!

    24 引用 • 373 回帖 • 1 关注
  • 导航

    各种网址链接、内容导航。

    37 引用 • 168 回帖 • 1 关注
  • Bootstrap

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

    18 引用 • 33 回帖 • 669 关注
  • PostgreSQL

    PostgreSQL 是一款功能强大的企业级数据库系统,在 BSD 开源许可证下发布。

    22 引用 • 22 回帖
  • AngularJS

    AngularJS 诞生于 2009 年,由 Misko Hevery 等人创建,后为 Google 所收购。是一款优秀的前端 JS 框架,已经被用于 Google 的多款产品当中。AngularJS 有着诸多特性,最为核心的是:MVC、模块化、自动化双向数据绑定、语义化标签、依赖注入等。2.0 版本后已经改名为 Angular。

    12 引用 • 50 回帖 • 457 关注
  • Ruby

    Ruby 是一种开源的面向对象程序设计的服务器端脚本语言,在 20 世纪 90 年代中期由日本的松本行弘(まつもとゆきひろ/Yukihiro Matsumoto)设计并开发。在 Ruby 社区,松本也被称为马茨(Matz)。

    7 引用 • 31 回帖 • 202 关注
  • Q&A

    提问之前请先看《提问的智慧》,好的问题比好的答案更有价值。

    7274 引用 • 33170 回帖 • 203 关注
  • MongoDB

    MongoDB(来自于英文单词“Humongous”,中文含义为“庞大”)是一个基于分布式文件存储的数据库,由 C++ 语言编写。旨在为应用提供可扩展的高性能数据存储解决方案。MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。它支持的数据结构非常松散,是类似 JSON 的 BSON 格式,因此可以存储比较复杂的数据类型。

    90 引用 • 59 回帖
  • Google

    Google(Google Inc.,NASDAQ:GOOG)是一家美国上市公司(公有股份公司),于 1998 年 9 月 7 日以私有股份公司的形式创立,设计并管理一个互联网搜索引擎。Google 公司的总部称作“Googleplex”,它位于加利福尼亚山景城。Google 目前被公认为是全球规模最大的搜索引擎,它提供了简单易用的免费服务。不作恶(Don't be evil)是谷歌公司的一项非正式的公司口号。

    49 引用 • 192 回帖 • 1 关注
  • MySQL

    MySQL 是一个关系型数据库管理系统,由瑞典 MySQL AB 公司开发,目前属于 Oracle 公司。MySQL 是最流行的关系型数据库管理系统之一。

    675 引用 • 535 回帖
  • 分享

    有什么新发现就分享给大家吧!

    246 引用 • 1781 回帖