首页 > 编程语言 >C++需要把析构函数定义为虚函数的场景

C++需要把析构函数定义为虚函数的场景

时间:2024-10-18 10:35:12浏览次数:1  
标签:调用 函数 基类 把析构 C++ Base 析构 派生类

C++需要把析构函数定义为虚函数的场景

content

C++ 中将析构函数定义为虚函数的作用

在 C++ 中,将析构函数定义为虚函数(virtual的主要目的是为了在多态(polymorphism)场景下正确地销毁对象,避免内存泄漏或资源未正确释放的问题。


1. 问题背景:非虚析构函数的风险

在 C++ 中,当你通过基类指针引用指向一个派生类对象,并且使用基类指针删除该对象时,如果析构函数不是虚函数,只会调用基类的析构函数,而不会调用派生类的析构函数。这会导致派生类的资源没有被正确释放,可能造成内存泄漏。

示例:非虚析构函数的错误行为

#include <iostream>
using namespace std;

class Base {
public:
    ~Base() {
        cout << "Base destructor called" << endl;
    }
};

class Derived : public Base {
public:
    ~Derived() {
        cout << "Derived destructor called" << endl;
    }
};

int main() {
    Base* obj = new Derived();  // 使用基类指针指向派生类对象
    delete obj;  // 只调用了 Base 的析构函数
    return 0;
}

输出:

Base destructor called

解释:

  • obj 是一个指向 Derived 对象的基类指针,但由于 Base 的析构函数不是虚函数,当我们通过 delete 销毁对象时,只调用了 Base 的析构函数,而派生类的析构函数未被调用
  • 这可能导致派生类的资源(如动态分配的内存、文件句柄等)未正确释放

2. 解决方案:将析构函数定义为虚函数

通过将基类的析构函数定义为虚函数,可以确保在通过基类指针删除派生类对象时,先调用派生类的析构函数,然后调用基类的析构函数

示例:虚析构函数的正确行为

#include <iostream>
using namespace std;

class Base {
public:
    virtual ~Base() {
        cout << "Base destructor called" << endl;
    }
};

class Derived : public Base {
public:
    ~Derived() {
        cout << "Derived destructor called" << endl;
    }
};

int main() {
    Base* obj = new Derived();  // 使用基类指针指向派生类对象
    delete obj;  // 正确调用了派生类和基类的析构函数
    return 0;
}

输出:

Derived destructor called
Base destructor called

解释:

  • 由于基类的析构函数被声明为虚函数,当我们通过基类指针删除派生类对象时,程序会根据对象的实际类型(即 Derived)调用正确的析构函数。
  • 派生类的析构函数会先被调用,然后调用基类的析构函数,确保所有资源被正确释放。

3. 为什么析构函数必须是虚函数?

多态环境下(即使用基类指针或引用操作派生类对象的场景),如果基类的析构函数不是虚函数,会导致:

  • 派生类的析构函数不会被调用,资源无法正确释放。
  • 内存泄漏,如派生类动态分配的内存无法释放。
  • 资源泄露,如文件句柄未关闭、网络连接未释放等。

将析构函数定义为虚函数,确保即使我们通过基类指针操作派生类对象,也能正确地调用派生类的析构函数。


4. 纯虚析构函数

如果基类是一个抽象类(不能实例化),你可以将析构函数声明为纯虚函数,但必须提供定义(否则链接时会报错)。

示例:纯虚析构函数

#include <iostream>
using namespace std;

class Base {
public:
    virtual ~Base() = 0;  // 纯虚析构函数
};

Base::~Base() {
    cout << "Base destructor called" << endl;
}

class Derived : public Base {
public:
    ~Derived() {
        cout << "Derived destructor called" << endl;
    }
};

int main() {
    Base* obj = new Derived();
    delete obj;
    return 0;
}

输出:

Derived destructor called
Base destructor called

解释:

  • 纯虚析构函数的存在意味着该类是抽象类,无法直接实例化。
  • 即便是纯虚函数,也需要提供析构函数的定义,以确保派生类对象销毁时正确调用基类析构函数。

5. 总结

将析构函数定义为虚函数的作用:

  1. 保证多态环境下的正确析构:通过基类指针删除派生类对象时,会调用派生类的析构函数。
  2. 避免内存泄漏:确保所有资源被正确释放。
  3. 支持抽象类:析构函数可以是纯虚函数,使类成为抽象类。

何时需要虚析构函数:

  • 多态使用场景:如果类被设计为基类,并且会通过基类指针或引用操作派生类对象,则必须将析构函数声明为虚函数
  • 如果类不会被继承,或者不会通过基类指针操作派生类对象,可以不将析构函数声明为虚函数,以节省一些性能开销

在 C++ 的继承体系中,虚析构函数是保证资源正确释放的重要机制,必须在设计时谨慎考虑。

强调

  • 纯虚析构函数的存在意味着该类是抽象类,无法直接实例化。
  • 即便是纯虚函数,也需要提供析构函数的定义,以确保派生类对象销毁时正确调用基类析构函数。

标签:调用,函数,基类,把析构,C++,Base,析构,派生类
From: https://www.cnblogs.com/smartljy/p/18473763

相关文章

  • MySQL基础函数的学习要点和步骤
    MySQL基础函数的学习要点和步骤可以归纳为以下几个方面:学习要点1.函数的概念:  -函数是一段可以直接被另一段程序调用的程序或代码。2.分类理解:  -字符串函数:如`CONCAT()`,`LOWER()`,`UPPER()`,`LPAD()`,`RPAD()`,`TRIM()`,`SUBSTRING()`等,用于处理字符串......
  • 南沙C++信奥赛陈老师解一本通题 1284:摘花生
    ​【题目描述】HelloKitty想摘点花生送给她喜欢的米老鼠。她来到一片有网格状道路的矩形花生地(如下图),从西北角进去,东南角出来。地里每个道路的交叉点上都有种着一株花生苗,上面有若干颗花生,经过一株花生苗就能摘走该它上面所有的花生。HelloKitty只能向东或向南走,不能向西或......
  • 每日OJ题_牛客_非对称之美_最长非回文字符串_C++_Java
    目录牛客_非对称之美_最长非回文字符串题目解析C++代码Java代码牛客_非对称之美_最长非回文字符串非对称之美(nowcoder.com)题目解析找到规律就是最长非回文字符串(判断是否全同->0,否则是n-1(回文减去1)或n)。C++代码#include<iostream>usingnamespacestd;int......
  • 每日OJ题_牛客_连续子数组最大和_线性dp_C++_Java
    目录牛客_连续子数组最大和_线性dp题目解析C++代码Java代码牛客_连续子数组最大和_线性dp连续子数组最大和_牛客题霸_牛客网(nowcoder.com)描述:        给定一个长度为 n的数组,数组中的数为整数。请你选择一个非空连续子数组,使该子数组所有数之和尽可能大,......
  • Chromium form表单post提交过程分析c++
    form表单get提交参考之前文章Chromium前端form表单提交过程分析c++-CSDN博客一、表单post提交测试例子:<!DOCTYPEhtml><html><head><meta charset="utf-8"> <title>菜鸟教程(runoob.com)</title> </head><body><h2>表单</h2>......
  • C/C++语言基础--C++四大类型转换讲解
    本专栏目的更新C/C++的基础语法,包括C++的一些新特性前言通过前面几节课,我们学习了抽象、封装、继承、多态、异常等概念,这一篇我们将继续学习C++的类型转换,和C语言还有很大区别的;在本节课最后,也简要说了一下在计算机视角上看类型是什么样子的;C语言后面也会继续更新知识点,......
  • Java 一维数组作为函数参数
    //一维数组的引用#defineSIZE5voidinput(inta[],intlen);voidoutput(inta[],intlen);//函数的声明intmain(void){   inti=0;   intarr[SIZE]={86,85,85,896,45};//同理五个数据只是偶然,可能会更多   //输入   input(arr,SIZE);......
  • vecode写c++遇到窗口一闪而过+中文乱码咋办
    本人没使用系统cmd窗口,而是使用了vscode内置终端,目的是为了之后输出中文的时候不乱码(vscode是utf-8,cmd是gbk,干脆全部使用vscode,不使用系统cmd作为输出窗口)附上配置文件:launch.json{//使用IntelliSense了解相关属性。//悬停以查看现有属性的描述。//欲了解......
  • c++ --类和对象(篇1)
    目录1.类的概念:2.类的定义3.对象的创建:4.封装5.继承1.类的概念:                类用于指定对象的形式,是一种用户自定义的数据类型,                它是一种封装了数据和函数的组合。类中的数据称为成员变量,函数称为成员函数。 ......
  • 【算法】C++中的二分查找
    ......