为什么析构函数必须是虚函数?为什么默认的析构函数不是虚函数?

2023-10-30

本博客可能随时删除或隐藏,请关注微信公众号,获取永久内容。

微信搜索“编程笔记本”,获取更多信息
------------- codingbook2020 -------------

今天我们来谈一谈面试 C++ 工程师时经常被谈到的一个问题:为什么析构函数必须是虚函数?为什么默认的析构函数不是虚函数?

首先,我们看一下百度百科对虚函数是怎么定义的:

在某基类中声明为 virtual并在一个或多个派生类中被重新定义的成员函数,用法格式为:virtual 函数返回类型 函数名 ( 参数表 ) { 函数体 };实现多态性,通过指向派生类的基类指针或引用,访问派生类中同名覆盖成员函数。

好了,现在我们大概知道什么是虚函数,虚函数就是类中使用关键 virtual修饰的成员函数,其目的是为了实现多态性。

那么什么是多态性呢?

所谓多态性,顾名思义就是“多个性态”。更具体一点的就是,用一个名字定义多个函数,这些函数执行不同但相似的工作。最简单的多态性的实现方式就是函数重载模板,这两种属于静态多态性。还有一种是动态多态性,其实现方式就是我们今天要说的虚函数

回归正题。

一、为什么析构函数必须是虚函数?

当然了,这么说其实是不太严谨的,因为我完全可以将析构函数定义成非虚函数。这个我们后面再说。

首先我们需要知道析构函数的作用是什么。析构函数是进行类的清理工作,具体来说就是释放构造函数开辟的内存空间和资源,当然我们完全可以在析构函数中进行任何我们想要的操作,比如下面我们给出的示例代码,就在析构函数中打印提示信息。

前面我们在介绍虚函数的时候就说到,为实现多态性,可以通过基类的指针或引用访问派生类的成员。也就是说,声明一个基类指针,这个基类指针可以指向派生类对象

下面我们来看一个例子:

#include <iostream>

using namespace std;

class Father {
public:
    ~Father() {
        cout << "class Father destroyed" << endl;
    }
};

class Son : public Father {
public:
    ~Son() {
        cout << "class Son destroyed" << endl;
    }
};

int main() {
    Father* p = new Son;
    delete p;

    return 0;
}

/*
运行结果:
class Father destroyed
*/

上面的示例程序中,我们定义了两个类,一个基类,一个派生类,派生类公有继承父类。为了描述简单,这两个类只定义了析构函数,并在析构函数中输出提示信息。在主函数中,我们声明了一个基类的指针,并用一个派生类的实例去初始化这个基类指针,随后删除这个指针。我们看到程序运行的结果,只有基类的析构函数被调用。

为什么会这样呢?指针明明指向的是派生类对象,那删除这个指针,为何只有基类的析构函数被调用,而派生类的析构函数却没有调用呢?

我们先把问题留在这里,接下来我们看看,若析构函数被定义成虚函数会怎么样呢?

#include <iostream>

using namespace std;

class Father {
public:
    virtual ~Father() {
        cout << "class Father destroyed" << endl;
    }
};

class Son : public Father {
public:
    ~Son() {
        cout << "class Son destroyed" << endl;
    }
};

int main() {
    Father* p = new Son;
    delete p;

    return 0;
}

/*
运行结果:
class Son destroyed
class Father destroyed
*/

当基类的析构函数被定义成虚函数时,我们再来删除这个指针时,先调用派生类的析构函数,再调用基类的析构函数,很明显这才是我们想要的结果。因为指针指向的是一个派生类实例,我们销毁这个实例时,肯定是希望即清理派生类自己的资源,同时又清理从基类继承过来的资源。而当基类的析构函数为非虚函数时,删除一个基类指针指向的派生类实例时,只清理了派生类从基类继承过来的资源,而派生类自己独有的资源却没有被清理,这显然不是我们希望的。

所以说,如果一个类会被其他类继承,那么我们有必要将被继承的类(基类)的析构函数定义成虚函数。这样,释放基类指针指向的派生类实例时,清理工作才能全面进行,才不会发生内存泄漏。

二、为什么默认的析构函数不是虚函数?

那么既然基类的析构函数如此有必要被定义成虚函数,为何类的默认析构函数却是非虚函数呢?

首先一点,语言设计者如此设计,肯定是有道理的。

原来是因为,虚函数不同于普通成员函数,当类中有虚成员函数时,类会自动进行一些额外工作。这些额外的工作包括生成虚函数表虚表指针,虚表指针指向虚函数表。每个类都有自己的虚函数表,虚函数表的作用就是保存本类中虚函数的地址,我们可以把虚函数表形象地看成一个数组,这个数组的每个元素存放的就是各个虚函数的地址。
这样一来,就会占用额外的内存,当们定义的类不被其他类继承时,这种内存开销无疑是浪费的。

这样一说,问题就不言而喻了。当我们创建一个类时,系统默认我们不会将该类作为基类,所以就将默认的析构函数定义成非虚函数,这样就不会占用额外的内存空间。同时,系统也相信程序开发者在定义一个基类时,会显示地将基类的析构函数定义成虚函数,此时该类才会维护虚函数表和虚表指针。

这个问题至此就解释完成了,祝大家面试顺利!

本博客可能随时删除或隐藏,请关注微信公众号,获取永久内容。

识别下方二维码关注我,或微信搜索**“编程笔记本”**,获取更多信息。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么析构函数必须是虚函数?为什么默认的析构函数不是虚函数? 的相关文章

随机推荐

  • 日常健康管理

    0分贝0dB是人们刚刚能听到的最微弱的声音听觉下限 30dB40dB是较为理想的安静睡眠环境 超过50dB会影响睡眠和休息70dB会干扰谈话 影响工作效率长期生活在90分贝以上的噪声环境 会严重影响听力和引起神经衰弱 头疼 血压升高等疾如果
  • 怎么看空调定时成功_空调定时怎么设置

    空调是我们常用的电器 尤其是在炎热的夏季空调是我们必须使用的家电 夜晚的时候一直开着空调很容易着凉 这个时候我们可以开启空调的定时键 今天想跟大家说一说空调定时怎么设置 希望可以给大家带来帮助 一 空调定时怎么设置 1 首先我们先打开空调
  • CSV文件中的逗号、双引号的转义。读写带特殊字符的csv文件。

    如果字段中有逗号 该字段使用双引号 括起来 如果该字段中有双引号 该双引号前要再加一个双引号 然后把该字段使用双引号括起来 字段处理前 字段处理后 abc d2 abc d2 ab c d2 ab c d2 abc abc 参考文章 htt
  • STM32F103ZET6【HAL函开发】STM32CUBEMX------2.GPIO输入、按键外部中断

    一 硬件介绍 正点原子战舰开发板 主控芯片STM32F103ZET6 两个LED接到PB5 PE5 三个按键PE2 PE3 PE4接GND 一个按键PA0接VCC3 3 二 STM32CUBEMX基础配置 2 1 晶振配置 如果你的板子上外
  • DC-DC模块输入端电容对12V电源纹波的影响

    1 目标 说明DC DC模块输入端极性电容对供电源纹波的影响 强调DC DC输入端极性滤波电容的重要性 2 DC DC模块介绍 DC DC模块的输入电源大小为12V 核心芯片为MP24943 输出电源大小为5V 电路原理图如下 输入端不添加
  • Java千百问_04异常处理(007)_常见的java异常有哪些(非运行时)

    1 常见的java运行时异常有哪些 了解非运行时异常看这里 什么是java中的异常 常见的运行时异常看这里 常见的运行时异常有哪些 我们所说的常见异常是jdk或者其他常用第三方jar中的异常 出现频次很高的异常 常见的非运行时异常 即检查异
  • 2019 校招多益网络软件开发java 笔试题

    刚做完 真的又凉了 编程题没写出来 心累了 而且问答题好多其实都是之前看过的 可是只有模糊的印象 则真的好气呀 也算给自己敲了警钟吧 要用心记 用心总结 多益的笔试题型挺多的 难度算还好的 毕竟只有一道编程题 奈何我没写出来 题型 选择题
  • 15b万用表怎么测电容_万用表怎么用?福禄克15B+一机详解万用表的使用方法

    随着大众消费等级和安全意识的不断提升 在不断加购电器的同时 人们愈发重视电路安全 万用表渐渐从电工行业专用仪器成为了家庭标配工具之一 然而 很多人把万用表买回家之后 却对着表盘上密密麻麻的符号一筹莫展 最后只能放在角落里落满灰尘 那么 万用
  • Mysql索引

    创建索引 索引能提高查询速率 1 最常见的是主键 默认加了索引 2 普通创建索引方法 create index 名字 on 表名 列名 3 联合索引 create index 索引名 on 表名 列名 列名 列名 4 复合索引查询的时候遵守
  • 二分的经典问题 最大化最小值和最小化最大值

    有点长 可以选自己想看的部分看 不过建议把刚开始的介绍看完 不多说 先来一个在升序无重复元素的数组中二分搜索的板子 l r 2 mid可能会爆int这种细节问题我们就放一边 int a MAX int Binary Search int v
  • 文件上传——七牛云

    目录 七牛云简介 七牛云使用前 使用七牛云SDK开发 开发步骤 开发接口测试 源码下载 七牛云简介 七牛云是国内领先的企业级公有云服务商 致力于打造以数据为核心的场景化PaaS服务 围绕富媒体场景 七牛先后推出了对象存储 融合CDN加速 数
  • 【电气专业知识问答】问:电动机轴承温度异常升高如何处理?

    电气专业知识问答 问 电动机轴承温度异常升高如何处理 答 1 起因 电动机轴承温度异常升高的可能原因 油环卡住 轴瓦上油槽被杂物堵塞或者被磨平 轴承冷却水 如有 发生故障 润滑不良 缺油 加油过多 油不清洁 油中有水分 等 电动机的端盖装配
  • 【第02例】IPD进阶

    目录 简介 专栏目录 内容详解 作者简介 CSDN学院相关内容 简介 今天继续来讲讲 IPD 中涉及的几个评审点 PDCP 是英文 Plan Decision Check Point 的英文首字母的简称 也就是计划决策评审点 具体讲解 PD
  • Server MyEclipse Tomcat v8.5 was unable to start within 45 seconds.

    MyEclipse启动Tomcat v8 5遇到问题 解决方法之一 直接在MyEclipse上修改 1 在下图中定位到Servers下 双击 MyEclipse Tomcat v8 5 2 在下图中点红框部分 3 在下图中 默认Start的
  • 参数显著性检验的p值小于显著性水平不等于其具有经济学意义

    在做简单线性回归或者多元线性回归时 如何评估参数的统计意义和经济意义是我们研究问题的两个重要方面 理论意义和经济意义是如何显示在数字上的呢 以下是笔者在做相关或者线性回归课题时学习整理出来的 在此分享记录 参数的t统计量足够大 或者p值足够
  • python 接口自动化测试-----常见面试题汇总

    1 软件接口是什么 程序不同模块之间传输数据并作处理的类或函数 2 HTTP 和 HTTPS 协议区别 答 https 协议需要到 CA Certificate Authority 证书颁发机构 申请证书 一般免费证书 较少 因而需要一定费
  • 【Anaconda】基本操作

    1 创建 使用命令查看当前拥有的虚拟环境 conda info envs 在指定目录下创建新的虚拟环境 其中C ProgramData Anaconda3 envs 是创建的目录所在位置 pytorch 是新环境名称 python 3 8是
  • SpringBoot之【mybatisplus】分页插件、条件查询、sql打印开启

    文章目录 一 概述 二 流程 1 sql打印开启 2 分页插件 3 常用条件 一 概述 本篇主要写开启 sql的打印 分页插件开启 条件查询 二 流程 1 sql打印开启 yml文件添加如下配置 mybatis plus 配置slq打印日志
  • ForeFront Chat 免费版GPT-4来了!

    Forefront Chat简介 近日 Forefront AI 正式推出 Forefront Chat 允许用户免费体验GPT 4 的强大功能 Forefront AI 在 Twitter 上表示 今天 我们发布了 Forefront C
  • 为什么析构函数必须是虚函数?为什么默认的析构函数不是虚函数?

    本博客可能随时删除或隐藏 请关注微信公众号 获取永久内容 微信搜索 编程笔记本 获取更多信息 codingbook2020 今天我们来谈一谈面试 C 工程师时经常被谈到的一个问题 为什么析构函数必须是虚函数 为什么默认的析构函数不是虚函数