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声明 的相关文章

随机推荐

  • 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 可通过基类的指针指