基础面试题:为什么将析构函数定义为虚析函数

2023-11-09

在C++中,有多继承关系,为什么要将析构函数定义成虚析函数 

目录

1、在有继承关系中,为什么将析构函数定义为虚函数

2、问题解答

3、总结


1、在有继承关系中,为什么将析构函数定义为虚函数

在面试的时候,是否非常常被问到,在有继承关系中为什么要将析构函数定义为虚析函数,在此运用场景下,这是有特殊原因的。析构函数作为对象生命周期时最后一个所见的函数,我们在析构函数中做些清理工作或者内存释放、重置等工作。它犹如一个清洁工,自动打扫卫生,当然前提是你delete 这个对象或对象的生命周期结束。


2、问题解答

我们常常通过声明的基类对象指针访问接口,这样做得目的是为了代码的一致性和统一

如:

class Base

{

        public virtual void update(){}

        virtual ~Base(){}

}

class Monster: public Base

{

        public virtual void update(){}

        virtual ~Monster(){}

}

public Hero :public Base

{

        public virtual void update(){}

        virtual ~Hero(){}

}

void main()

{

        vector<Base*> array;

        array.add(new Monster());

        array.add(new Hero ());

        //通过统一的接口访问update函数

        for(int i=0; i<array.size(); i++)

        {

                array[i]->update();

        }  

}

如上代码我们看到,往往我们需要通过数组或容器批量访问对象所指向类的函数,以求方便快捷

不然就只能这么写

        Monster* pMonster = new Monster();

        pMonster->update();

        Hero * pHero = new Monster();

        pHero ->update();

如果有千万个对象,那么就要写上几千万行如上代码,极其糟糕,维护起来也非常复杂。 所以在稍微复杂点的功能或系统中,我们需要统一的调用方式来实现代码一致性和简洁,将生成子类对象赋值给父类指针。

如:Base* p = new Monster();

但是这引发一个问题,我们知道new必须与delete 相对对应,空间生成与删除是一对欢喜冤家,new一个对象,必须一定要有一个delete相匹配; 当对象指针已经放入容器或作为参数传递时已经比较难于知道该指针所指向原来的所属那个类了(当然也可以知道,通过 dynamci_cast比对的方式,这又是个糟糕的主意)

如果我们不将析构函数声明成virtual函数时,在delete p的时候会怎么样呢?

class Base

{

        public virtual void update(){}

        ~Base(){}

}

public Hero :public Base

{

        private char* name;

       public Hero()

        {

                name = new char[32];

                memset(name,0,32);

        }

        public virtual void update(){}

        //重置函数

        private void reset(){}

        //情况1

        ~Hero ()

        {

                

                delete[] name;

                name = null;

                reset();

        }

        //情况2

        //virtual ~Hero ()

        //{

                //delete[] name;

                //name = null;

                reset();

               

        //}

}

void main()

{

        Base* p = new Hero();

        /*

        按照函数调用顺序原则(如果不知道的同学看下基础面试题:C++ 如何理解虚函数表

        中的找到函数调用查找循序)

        */

        delete p;

}

        在情况1 delete p的时候,只会调用Base:~Base()析构函数,而不会调用Hero::~Hero*()析构函数,这样就会造成内存泄露,name的空间不会被释放掉,每new 出一个Hero对象,就内存泄露32个Byte内存空间。

        在情况2 delete p的时候 会调用Hero::~Hero()析构函数。name的内存空间得以释放。


3、总结

        由于析构函数往往做些清理工作,在有继承关系时,相关类的析构函数需要定义成虚析构函数,以防止清理工作出现遗漏等情况。当然可能产生内存泄露只是问题一部分,还有其他可能因为清理工作做得不够细致,变量值没有恢复或重置而照成访问数据不正确问题,这里就不做赘述。


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

基础面试题:为什么将析构函数定义为虚析函数 的相关文章

  • EF Core 返回 null 关系,直到直接访问

    我有一些如下所示的模型 public class Mutant public long Id get set Relations public long OriginalCodeId get set public virtual Origi
  • copy_from_user() 错误:目标大小太小

    我正在为内核模块编写 ioctl 处理程序 我想从用户空间复制数据 当我编译禁用优化的代码时 O0 gflags 编译器返回以下错误 include linux thread info h 136 17 error call to bad
  • C++中类成员函数相互调用有什么好处?

    我是 C 新手 我发现下面的编程风格对我来说很有趣 我在这里写了一个简化版本 include
  • 从结构调用 C++ 成员函数指针

    我找到了有关调用 C 成员函数指针和调用结构中的指针的信息 但我需要调用结构内部存在的成员函数指针 但我无法获得正确的语法 我在类 MyClass 的方法中有以下代码片段 void MyClass run struct int MyClas
  • 如何以编程方式删除受信任的根证书颁发机构中的证书?

    我需要能够从组织中的每台电脑中删除特定的证书 是的 我可以逐个座位 但我要到周四才能完成 而且我没有人力逐个座位 是否有使用 C 的编程方式来执行此操作 我认为你不需要编写任何 C 看看certmgr exe del http msdn m
  • 获取列表框中视图中的项目

    我有一个 ListBox 其属性 VirtualizingStackPanel VirtualizationMode 设置为 回收 我正在绑定一个自定义集合 实现IList and IList
  • 如何在 Linux 上重新实现(或包装)系统调用函数?

    假设我想完全接管 open 系统调用 也许要包装实际的系统调用并执行一些日志记录 一种方法是使用 LD PRELOAD http scaryreasoner wordpress com 2007 11 17 using ld preload
  • 用于 C++ 中图像分析的 OpenCV 二进制图像掩模

    我正在尝试分析一些图像 这些图像的外部周围有很多噪声 但内部有一个清晰的圆形中心 中心是我感兴趣的部分 但外部噪声正在影响我对图像的二进制阈值处理 为了忽略噪音 我尝试设置一个已知中心位置和半径的圆形蒙版 从而使该圆之外的所有像素都更改为黑
  • X 轴和 Z 轴上的 Quaternion.Slerp,无 Y 轴

    I am trying to rotate the Player about X Y and Z axis The Y axis should not move from last angle Example if I rotate 45
  • ASP.NET - Crystal Report Viewer 打印按钮在 ASP.NET 中不起作用

    我正在使用 Visual Studio 2008 但我遇到了水晶报告问题 当我单击打印按钮时 它会将我带到弹出窗口 但未找到页面 弹出的网址是 http localhost aspnet client System Web 2 0 5072
  • 更改私有模块片段是否会导致模块重新编译?

    On 此页面有关 C 20 模块功能 https www modernescpp com index php c 20 modules private module fragment and header units 我发现了这样的说法 借
  • main.cpp 是必需的吗?

    我试图编译一个程序cmake 我最终删除了我的main cpp文件 我刚刚将其复合到另一个包含我的项目名称的文件中 即 我刚刚将主函数剪切并粘贴到该文件中 问题是我有一个main cpp未发现错误 不确定是否在C 一个名为main cpp是
  • 运行实体框架自定义工具,它有什么作用?

    在 Visual Studio 中 当使用实体框架并为 tt 和 Context tt 文件应用运行自定义工具时 它是什么以及它有什么作用 为什么它解决数据库同步问题 有时 为什么我应该在运行 tt 之前运行它 Context tt 它被称
  • 具有多个父项的 Qt 树模型

    我想构建一棵树 其中一个元素可以引用另一个元素 我想要构建的树是 像这样的东西 A B C D E F P this is a pointer to C D first child of C E second child of C I fo
  • g++ / gcc 是否支持 C++20 新的atomic_flag 功能?

    根据参考参数 https en cppreference com w cpp atomic atomic flag c 20 有丰富的 对我来说有用的 支持atomic flag运营 然而 目前尚不清楚 gcc 是否支持这些功能 它们在任何
  • 使用未命名命名空间而不是静态命名空间

    我可以假设在未命名命名空间中声明的对象相当于static namespace int x 1 static int x 2 FWIK 在这两种情况下 x将具有静态存储期限和内部链接 声明为的对象的所有规则也是如此static适用于未命名名称
  • 查找数组中的多个索引

    假设我有一个像这样的数组 string fruits watermelon apple apple kiwi pear banana 是否有一个内置函数可以让我查询 apple 的所有索引 例如 fruits FindAllIndex ap
  • 为什么存在系统调用

    我一直在阅读有关系统调用及其在 Linux 中如何工作的内容 我还有更多的阅读要做 但我读过的一件事都没有回答 那就是 为什么我们需要系统调用 我知道系统调用是用户空间程序要求内核执行某些操作的请求 但我的问题基本上是 为什么用户空间程序本
  • 如何配置 qt Creator 以显示 C++ 代码而不是反汇编程序?

    昨天我做了很多事情 比如更新 GCC Clang 和重新安装 Qt Creator 今天 在逐步调试我的代码时 调试器显示的是反汇编代码 而不是我编写的 C 代码 紧迫F10 or F11 调试器正在进入汇编代码而不是 cpp nor h我
  • 如何将模型绑定到动态创建的类 nancyfx

    首先感谢任何愿意查看我的问题的人 我对 Nancyfx 还很陌生 在尝试将 JSON 有效负载绑定到动态创建的类时遇到问题 我按照这篇文章中的代码动态创建了该类 在C 中动态创建一个类 https stackoverflow com que

随机推荐

  • json-简单示例

    json simple 是一个简单的 JSON java 工具包 json simple 库完全符合 JSON 规范 RFC4627 json 简单 json simple uses Map and List internally for
  • 如何在R中使用sample()获取样本?

    让我们了解 R 中最常用的函数之一 sample 在数据分析中 对数据进行采样是分析师最常见的过程 要研究和理解数据 有时采样是最好的方法 对于大数据来说尤其如此 R 提供标准函数sample 来从数据集中获取样本 许多业务和数据分析问题都
  • 如何在 Ubuntu 16.04 上使用 Webmin、LAMP、BIND 和 PostFix 安装 Virtualmin

    介绍 Webmin是一个 Web 前端 允许您通过浏览器远程管理服务器 虚拟分钟是Webmin的一个插件 可以通过单个界面简化多个虚拟主机的管理 类似于cPanel or Plesk 使用 Virtualmin 您可以管理用户帐户 Apac
  • Python等待时间,等待用户输入

    有时我们希望 python 程序在执行下一步之前等待特定的时间 我们可以用时间模块 sleep 函数将我们的程序暂停指定的秒数 Python等待时间 让我们看一个简单的示例 在执行进一步的语句之前 我们将暂停程序 5 秒钟 import t
  • Java 队列 – Java 中的队列

    Java Queue is an interface available in java util package and extends java util Collection interface Just like Java List
  • Python 小数 - 除法、舍入、精度

    Python 小数模块帮助我们以适当的精度进行除法和四舍五入numbers Python 十进制模块 In this lesson on decimal module in Python we will see how we can man
  • Java 字符串中最长的回文子串

    字符串中最长的回文子串是很常见的java面试题 找出最长的回文数String 首先 我们需要确定这样做的逻辑 字符串算法中的最长回文子串 这里的关键点是 从任何回文串的中间开始 如果我们向右和向左移动 1 个位置 它总是相同的字符 例如12
  • 理解 Python 3 中的字典

    介绍 The 字典是Python内置的mapping类型 词典地图keys to values这些键值对提供了一种在 Python 中存储数据的有用方法 字典通常用于保存相关数据 例如 ID 或用户配置文件中包含的信息 字典的两侧都带有花括
  • 如何使用 cd、pwd 和 ls 探索 Linux 服务器上的文件系统

    介绍 熟悉命令行是利用 Linux 服务器平台强大功能的第一步 这也是您可能希望在此环境中执行的基本上所有与服务器相关的活动的基本先决条件 虽然许多工具都存在图形替代方案 但学习命令行是一项技能 可以让您以大多数 GUI 图形用户界面 无法
  • Nginx 位置指令示例

    NGINX 服务器块中的位置指令允许将请求路由到文件系统中的正确位置 该指令用于告诉 NGINX 在哪里查找资源 方法是在将位置块与 URL 进行匹配时包含文件和文件夹 在本教程中 我们将详细了解 NGINX 位置指令 先决条件 您已经按照
  • 使用 React Hooks 优化服务器请求

    介绍 作为一名 React 开发人员 您是否厌倦了与管理不善的服务器请求作斗争 别再犹豫了 反应钩子是 React 库的一项强大功能 它允许开发人员在功能组件中使用状态和其他 React 功能 并可以帮助优化服务器请求并将 Web 应用程序
  • 如何在 Ubuntu 14.04 上使用 uWSGI 和 Nginx 为 Flask 应用程序提供服务

    介绍 在本指南中 我们将在 Ubuntu 14 04 上使用 Flask 微框架设置一个简单的 Python 应用程序 本文的大部分内容将介绍如何设置 uWSGI 应用程序服务器来启动应用程序以及如何设置 Nginx 作为前端反向代理 先决
  • python爬虫需要学习哪些基础知识_Python爬虫学习线路图丨Python爬虫需要掌握哪些知识点...

    据不完全统计 世界上80 的爬虫都是基于Python开发的 Python简单易学 对编程初学者十分友好 而且具有丰富而强大的库 开发效率奇高 因此很多编程爱好者都对Python爬虫十分感兴趣 要知道学好爬虫对工作大有裨益 可为今后入门大数据
  • 基于flask框架的用户注册页面实例

    文件框架 app py文件 from flask import Flask render template request app Flask name app route def index return register 申请注册页面
  • GLES2.0中文API-glBindAttribLocation

    名称 glBindAttribLocation 将通用顶点属性索引与命名属性变量相关联 C规范 void glBindAttribLocation GLuint program GLuint index const GLchar name
  • 解决Nginx 404 not found

    简述 昨天网站还正常运营 今天突然发现网站打不开了 首页正常打开 其他栏目全部都404 not found 注意 我的问题是 昨天还正常运行 突然就不可以了 小伙伴们注意了 如果跟我的问题不一样 此篇博文就没有任何参考意义 解决思路 1 首
  • 进程调度相关

    进程的同步 机制原子操作 信号量机制 自旋锁 管程 会合 分布式系统 进程之间通信的途径 共享存储系统 消息传递系统 管道 以文件系统为基础 操作系统中进程调度策略 FCFS 先来先服务 优先级 时间片轮转 多级反馈
  • python元组添加元素_Python 元组_python元组操作_添加元素_python 元组转字符串

    Python 元组 Python的元组与列表类似 不同之处在于元组的元素不能修改 元组使用小括号 列表使用方括号 元组创建很简单 只需要在括号中添加元素 并使用逗号隔开即可 如下实例 tup1 physics chemistry 1997
  • ‘cnpm’不是内部或外部命令,也不是可运行的程序或批处理文件

    问题讲解 最近项目中涉及到了antd Ant Design of React 需要学习了解一下 首先是安装 安装完nodejs 通过 npm v 和 node v 命令查看安装情况 都没问题 因为在国内可以安装 cnpm 获得更快速 更安全
  • 基础面试题:为什么将析构函数定义为虚析函数

    在C 中 有多继承关系 为什么要将析构函数定义成虚析函数 目录 1 在有继承关系中 为什么将析构函数定义为虚函数 2 问题解答 3 总结 1 在有继承关系中 为什么将析构函数定义为虚函数 在面试的时候 是否非常常被问到 在有继承关系中为什么