c++中多态调用场景下基类析构函数的virtual声明

2023-05-16

文章目录

  • 一.基类析构函数未加virtual声明的情况
    • 1.1 基础示例演示
    • 1.2 进阶示例演示
  • 二.基类析构函数添加virtual声明的情况
  • 三.总结

一.基类析构函数未加virtual声明的情况

  在多态场景中,可通过基类的指针指向子类对象,并完成对子类对象的成员函数调用;在函数对象析构时,根据继承顺序,往往是先调用子类的析构函数,然后调用基类的析构函数,但是在多态场景中,可能会出现以下错误的情况:

1.1 基础示例演示

#include <iostream>

using namespace std;
 
class base
{
public:
    base(){cout<<"基类构造"<<endl;};
    ~base(){cout<<"基类析构"<<endl;};
};
 
class obj:public base
{
public:
    obj(){cout<<"子类构造"<<endl;};
    ~obj(){cout<<"子类析构"<<endl;};
};

int main()
{
    base *pbase=new obj;    // 创建一个基类指针,并指向子类对象
    delete pbase;           // 析构基类对象
    pbase=nullptr;

    return 0;
}

  上述程序看似没问题,但是执行下来发现,输出结果如下:
在这里插入图片描述
  可以发现此处在析构时仅仅调用了基类的析构函数,并没有调用子类的析构函数,在上述例子中并不会造成什么太坏影响,但是如果在子类的析构函数中做了内存释放、资源清楚等事情,上述情况就会造成内存泄漏,具体见下面的示例。

1.2 进阶示例演示

#include <iostream>
#include <string.h>

using namespace std;
 
class base
{
public:
    base(){cout<<"基类构造"<<endl;};
    ~base(){cout<<"基类析构"<<endl;};
    virtual void display(void) = 0;
};
 
class obj:public base
{
public:
    obj(){
        p = (char *)malloc(100);
        strcpy(p,"子类申请的内存空间调用");
        cout<<"子类构造"<<endl;
    };
    ~obj(){
        free(p);
        p=nullptr;
        cout<<"子类析构"<<endl;
    };
    virtual void display(void)
    {
        std::cout<<string(p)<<std::endl;
    };
private:
    char *p =nullptr;
};

int main()
{
    base *pbase=new obj;    // 创建一个基类指针,并指向子类对象
    pbase->display();       // 基类指针调用子类对象,多态
    delete pbase;           // 析构基类对象
    pbase=NULL;
    return 0;
}

程序执行结果:
在这里插入图片描述
  在上述示例中,可以发现在子类的构造函数中申请了100 byte的内存空间给指针p,在析构函数中释放了该内存,因此在执行子类构造函数后必须执行其析构函数,否则会造成内存泄漏,上述的执行结果显然没有调用子类的析构函数,必然造成p申请的内存空间得不到释放,造成内存泄漏,如何解决?基类析构函数需要加virtual声明。具体见下。

二.基类析构函数添加virtual声明的情况

  如何解决在多态调用的场景中确保子类的对象能够被及时析构------在基类的析构函数加virtual声明。

#include <iostream>
#include <string.h>

using namespace std;
 
class base
{
public:
    base(){cout<<"基类构造"<<endl;};
    virtual ~base(){cout<<"基类析构"<<endl;};    // !!! virtual声明很关键
    virtual void display(void) = 0;    
};
 
class obj:public base
{
public:
    obj(){
        p = (char *)malloc(100);
        strcpy(p,"子类申请的内存空间调用");
        cout<<"子类构造"<<endl;
    };
    ~obj(){
        free(p);
        p=nullptr;
        cout<<"子类析构"<<endl;
    };
    virtual void display(void)
    {
        std::cout<<string(p)<<std::endl;
        // std::cout<<"dasdasdsa"<<std::endl;
    };
private:
    char *p =nullptr;
};

int main()
{
    base *pbase=new obj;    // 创建一个基类指针,并指向子类对象
    pbase->display();       // 基类指针调用子类对象,多态
    delete pbase;           // 析构基类对象
    pbase=NULL;
    return 0;
}

程序执行结果:
在这里插入图片描述

  上述示例中,在基类的析构函数声明时添加了virtual关键字,可见执行结果中调用了子类的析构函数,自然在子类析构函数中释放了p申请的内存,避免了内存泄漏。

三.总结

(1)基类的的析构函数不是虚函数的话,删除指针时,只有基类的内存被释放,派生类的没有,这样就内存泄漏了。

(2)析构函数不是虚函数的话,直接按指针类型调用该类型的析构函数代码,因为指针类型是基类,所以直接调用基类析构函数代码。

(3)当基类指针指向派生类的时候,如果析构函数不声明为虚函数,在析构的时候,不会调用派生类的析构函数,从而导致内存泄露。

(4)子类对象创建时先调用基类构造函数然后在调用子类构造函数,在清除对象时顺序相反,所以delete pbase只清除了基类,而子类没有清除。

(5) 什么时候才要用虚析构函数呢?通常情况下,程序员的经验是,当类中存在虚函数时要把析构函数写成virtual,因为类中存在虚函数,就说明它有想要让基类指针或引用指向派生类对象的情况,此时如果派生类的构造函数中有用new/malloc动态产生的内存,那么在其析构函数中务必要delete这个数据,但是一般的像以上这种程序,这种操作只调用了基类的析构函数,而标记成虚析构函数的话,系统会先调用派生类的析构函数,再调用基类本身的析构函数

(6)一般情况下,在类中有指针成员的时候要写copy构造函数,赋值操作符重载和析构函数。

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

c++中多态调用场景下基类析构函数的virtual声明 的相关文章

  • 你可以重写私有虚拟方法吗?

    我认为你可以 但我的同事认为你不能 你甚至不能declare私有虚拟方法 唯一有意义的时候是如果你有 public class Outer private virtual void Foo public class Nested Outer
  • 抽象类:成员函数“virtual...”的抽象返回类型无效

    在我的程序中 我有这样的类层次结构 include
  • 状态模式 C++

    在遵循这里的一些优秀教程之后 我正在尝试创建一个简单的状态模式 http gameprogrammingpatterns com state html http gameprogrammingpatterns com state html
  • 未使用的私有虚拟方法是否允许将来扩展而不破坏 ABI 兼容性?

    我正在开发一个共享库 假设我有以下类定义 class MyClass public public interface private virtual void foo1 int virtual void foo2 int bool virt
  • iOS6 中的 CoreMIDI/PGMidi 虚拟 midi 错误

    面临两个错误 此代码在 iOS 4 和 5 中有效 但更新到 6 后 它不起作用 我发现了以下内容 但不知道如何在代码中修复它 从 iOS 6 开始 应用程序需要在其 UIBackgroundModes 中拥有音频键才能使用 CoreMID
  • 虚函数破坏私有访问

    我最近看到这篇文章IBM site http publib boulder ibm com infocenter comphelp v8v101 index jsp topic 2Fcom ibm xlcpp8a doc 2Flanguag
  • 将虚拟地址转换为物理地址

    The following page table is for a system with 16 bit virtual and physical addresses and with 4 096 byte pages The refere
  • 如何为 Mac OSX 编写虚拟打印机驱动程序 [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我需要为 OSX 编写一个 虚拟打印机驱动程序 以便当用户按 Command P 打开 打印 对话框时
  • Conda:创建虚拟环境

    我正在尝试创建一个虚拟环境 我已按照两者的步骤进行操作Conda https conda io docs user guide tasks manage environments html and Medium https medium c
  • C++ 中的虚方法调用是如何工作的?

    如何虚拟方法调用在 C 中工作 通过虚拟表 读这篇文章 http en wikipedia org wiki Virtual table http en wikipedia org wiki Virtual table 我可以在这里解释 但
  • 在虚拟 getter 中添加参数

    我想做的是这样的 Schema virtual getSomething get function what if what return this somethingElse else return this something what
  • 运行 WHM 时如何以正确的方式添加虚拟主机?

    我正在运行一台专用服务器 为我的客户使用 WHM 和 CentOS 5 分隔帐户 我的一个客户要求我安装 subversion 并将存储库存储在 webroot 下 repo 的真实文件夹将位于 home theirfolder svn 中
  • 强制虚拟方法表中函数的顺序?

    如何控制虚拟表中虚拟函数的顺序 它们的排列顺序是否与声明的顺序相同 当继承带有虚表的类时 继承类的虚表是基类的扩展 还是仅用继承类的虚函数创建的全新虚表 即虚拟表仍然位于类的索引 0x0 处吗 a 就标准而言 你不能 事实上你甚至不能假设
  • 为什么 Windows 为其系统地址空间保留 1Gb(或 2Gb)?

    众所周知 Windows 应用程序在 32 位系统上通常有 2Gb 的私有地址空间 使用 3Gb 开关可以将该空间扩展到 3Gb 操作系统自行保留剩余的 4Gb 我的问题是为什么 在内核模式下运行的代码 即设备驱动程序代码 有自己的地址空间
  • 避免派生类 C++ 中的“纯虚函数调用”

    我对 C 相当陌生 所以如果这个问题的水平稍微低于这里的通常标准 我想道歉 我试图让几个类从具有虚拟函数定义的基类继承 然后我想创建一个 MainClass 数组 它可以包含所有派生类 以便输出派生 定义的虚拟功能 我收到错误 R6025
  • Actionscript 3主类是根,但不允许动画虚拟相机

    我最近开始使用 Actionscript 3 学习 Animate CC 我正在尝试使用 Animate 的 虚拟相机 功能 为我提供一个可以平移 旋转和缩放游戏的相机 当根没有子类时 很容易实现 Camera 例如 您可以在屏幕上放置一个
  • C++中的虚继承是什么

    假设我有这样的代码 class A class B virtual public A class C virtual public A class D public B public C virtual public A If D继承B a
  • virtual对类模板成员使用的影响

    我 模糊地 知道模板不会被实例化 如果它是not used 例如 以下代码即使在以下情况下也能正常编译 T type没有意义时T int template
  • 如何破解虚拟表?

    我想知道如何更改地址Test它在虚拟表中HackedVTable void HackedVtable cout lt lt Hacked V Table lt lt endl class Base public virtual Test c
  • 有什么办法可以在抽象基类中拥有模板函数吗?

    我正在尝试创建一个配置管理器类 它可以通过 std string 存储任意对象 我的界面 抽象基类 的最初想法是这样的 当然这是非常不完整的 class ConfigurationManager public static boost sh

随机推荐

  • windows开机自启动frp教程

    https wp gxnas com 12153 html
  • ubuntu20.08下获取realsense内参

    ubuntu20 08下使用realsense过程记录 介绍一 安装SDK1 注册公钥2 将服务器添加到存储库列表中3 安装库4 安装开发者和调试包5 测试SDK 二 下载realsense2 camera包1 下载包2 测试包3 激活相关
  • 深度视场角(Depth Field of View)

    深度视场角 Depth Field of View Realsense相机深度图的建立依赖双目立体成像原理 xff0c 其有效视场是左 右成像器视场的重叠部分 xff0c 如下图所示 顺带一提 xff0c 这张图不能用于计算双目相机深度成像
  • ROS: Publisher and Subscriber

    通过上一节编写ROS的第一个程序hello world xff0c 我们对ROS的整个编程开发过程有了基本的了解 xff0c 现在我们就来编写真正意义上的使用ROS进行节点间通信的程序 由于之前已经建好了catkin ws这样一个工作空间
  • phpStorm2018安装教程

    1 鼠标右击 PhpStorm 2018 2 3 压缩包选择 解压到PhpStorm 2018 2 3 2 双击打开解压后的 PhpStorm 2018 2 3 文件夹 3 鼠标右击 PhpStorm 2018 2 3 exe 选择 以管理
  • 学习Java第一个星期感受和收获

    最近在学习java xff0c 学了有一个星期 xff0c 说一说这个星期的收获和总结吧 xff01 首先我也是从一个小白做起 xff0c 这个星期学习了很多 xff0c 很多java基础知识 xff0c 我印象比较深刻的是基本数据类型和引
  • MySql知识体系总结(2021版)

    一 MySQL三层逻辑架构 MySQL的存储引擎架构将查询处理与数据的存储 提取相分离 下面是MySQL的逻辑架构图 xff1a 1 第一层负责连接管理 授权认证 安全等等 每个客户端的连接都对应着服务器上的一个线程 服务器上维护了一个线程
  • freertos与linux区别,μClinux、μC/OS-II、eCos、FreeRTOS和djyos操作系统的特点及不足-嵌入式系统-与非网...

    基于 STM 平台且满足实时控制要求操作系统 xff0c 有以下 5 种可供移植选择 分别为 Clinux C xff0f OS II eCos FreeRTOS 和都江堰操作系统 djyos 下面分别介绍这五种嵌入式操作系统的特点及不足
  • SM4加密算法原理以及C语言实现

    文章目录 一 算法原理描述1 密钥及密钥参量 xff1a 2 加密算法 3 解密算法 xff1a 4 密钥扩展算法 xff1a 二 C语言算法实现 h部分代码 xff1a c部分代码 xff1a 一 算法原理描述 SM4分组密码算法是一个迭
  • SIM900A GPRS无线通信

    文章目录 一 模块介绍1 基本概况2 GPRS通信开发说明 二 TCP连接实现及其源码1 TCP连接实现方法2 程序源码 xff08 基于MSP430F149单片机 xff09 1 main c2 Config h及Config c3 SI
  • UCOSII-信号量与信号量集

    文章目录 一 前言1 任务间的同步2 事件 二 信号量1 信号与信号量介绍2 信号量常用函数3 信号量使用流程 xff08 互斥信号量和信号量两种 xff09 4 互斥型信号量使用5 使用一般信号量做任务同步 三 信号量集 事件标志组 1
  • UCOSII-消息邮箱与消息队列

    文章目录 一 事件控制块及事件处理函数1 等待任务列表2 事件控制块的结构3 操作事件控制块的函数4 空事件控制块列表 二 消息邮箱1 消息邮箱介绍2 消息邮箱操作步骤 三 消息队列1 消息指针数组2 队列控制块3 消息队列的操作流程 四
  • float型数据与4字节之间的转换

    文章目录 一 前言二 地址指针转换的方法三 共用体的方法 xff08 注意要定义全局变量数组s xff0c 即地址要分配为固定地址 xff09 一 前言 在与上位机之间进行数据收发 xff0c 要将float型数据转换成字节进行传输 xff
  • USB虚拟串口实现多字节数据接收,基于stm32h743

    文章目录 一 USB虚拟串口原理简介二 接收函数实现源码三 小结 一 USB虚拟串口原理简介 USB 虚拟串口 xff0c 简称 VCP xff0c 是 Virtual COM Port 的简写 xff0c 它是利用 USB 的 CDC 类
  • EC20/EC25 4G模块AT指令开发总结

    文章目录 一 EC25 20 4G模块简介二 AT指令总结1 通用AT指令2 建立TCP UDP连接相关AT指令 三 TCP传输数据流程四 UDP传输数据流程五 总结 一 EC25 20 4G模块简介 EC25 是一系列带分集接收功能的 L
  • C语言实现socket网络编程及多线程编程

    文章目录 一 概述二 TCP socket网络编程1 server端程序实现 xff08 tcp server cpp xff09 2 client端程序实现 xff08 tcp client cpp xff09 3 编译与执行 三 UDP
  • 基于openssl实现https双向身份认证及安全通信

    文章目录 一 概述二 代码设计2 1 ssl server c程序设计2 2 ssl client c程序设计 三 测试 一 概述 https基于SSL TLS提供安全的通信信道 xff0c 基于证书认证技术实现服务器和客户端之间的身份认证
  • ubuntu的不同版本

    ubuntu是现在最流行的Linux安装包 xff0c 本文介绍了ubuntu的各种版本 一 Ubuntu 每个ubuntu的版本都包含一个版本号 xff08 version number xff09 和一个代码名 xff08 code n
  • Linux下通过service服务管理用户进程

    文章目录 一 service配置介绍1 1 service配置文件1 2 配置文件的区块1 3 修改配置文件后重启1 4 服务管理 二 设计一个可执行程序三 设计一个service管理 home ubuntu test servicetes
  • c++中多态调用场景下基类析构函数的virtual声明

    文章目录 一 基类析构函数未加virtual声明的情况1 1 基础示例演示1 2 进阶示例演示 二 基类析构函数添加virtual声明的情况三 总结 一 基类析构函数未加virtual声明的情况 在多态场景中 xff0c 可通过基类的指针指