【C++指南】类和对象(三):类的默认成员函数——全面剖析: 析构函数

时间:2024-10-14 07:28:16

     ???? 博客主页:倔强的石头的****主页 

           ????Gitee主页:倔强的石头的gitee主页

            ⏩ 文章专栏:《C++指南》

                                  期待您的关注

 

47f09392526c71b5885ec838a3ea7ffe.gif

 

阅读本篇文章之前,你需要具备的前置知识:类和对象的基础

点击下方链接

【C++指南】类和对象(一):类和对象的定义和使用 基础讲解-****博客

在搞懂析构函数之前,建议阅读构造函数文章:(构造函数与析构函数配套使用)

点击下方链接:

 【C++指南】类和对象(二):类的默认成员函数——全面剖析构造函数-****博客

 系列后续文章:

【C++指南】类和对象(四):类的默认成员函数——全面剖析 拷贝构造函数-****博客

【C++指南】类和对象(五):类的默认成员函数——全面剖析 赋值运算符重载函数-****博客

 

目录

引言

析构函数的特性

默认析构函数的行为:

析构函数的规则

需要自己实现析构函数的情况:

示例代码:

实现析构函数需要考虑的问题:

结尾


 

 

引言

在C++编程中,析构函数是一个特殊的成员函数,它在对象的生命周期结束时被自动调用,用于执行清理工作,如释放对象占用的资源、关闭文件、解除动态分配的内存等。析构函数的存在确保了资源的正确管理和避免内存泄漏等问题。

本文将深入探讨C++析构函数的工作原理、特性、使用场景以及相关的最佳实践。

类似于前面的构造函数,想要掌握析构函数,也应该从这两个方面入手

  • 第⼀:我们不写时,编译器默认生成的函数行为是什么,是否满足我们的需求。
  • 第⼆:编译器默认⽣成的函数不满足我们的需求,我们需要自己实现,那么如何自己实现

 


 

析构函数的特性

  1. 名称:析构函数的名称必须与类名相同,但前面有一个波浪号(~)。例如,类 MyClass 的析构函数名称为 ~MyClass()

  2. 无返回类型:析构函数没有返回类型,包括 void

  3. 无参数:析构函数不接受任何参数。

  4. 自动调用:当一个对象的生命周期结束时,析构函数会被自动调用。这包括以下几种情况:

    • 局部对象离开其作用域时。
    • 动态分配的对象被 delete 释放时。
    • 全局或静态对象在程序结束时。
    • 对象作为类的成员被销毁时(例如,当包含该对象的另一个对象被销毁时)

 

默认析构函数的行为

如果类没有显式定义析构函数,编译器会生成一个默认析构函数。默认析构函数对于内置类型不做处理,对于自定义类型成员会调⽤他的析构函数·。

还需要注意的是:

我们显式写析构函数,对于⾃定义类型成员也会调用他的析构,也就是说⾃定义类 型成员⽆论什么情况都会⾃动调⽤析构函数

 

析构函数的规则

  1. 只能有一个析构函数:每个类只能有一个析构函数。

  2. 不能被重载析构函数不能被重载,因为它们的名称和参数列表是固定的。这一点不同于构造函数

  3. 不能被显式调用:析构函数不能被显式调用(除了通过 delete 释放动态分配的对象)。它们是由编译器自动调用的。

  4. 对象销毁顺序局部对象的销毁顺序与它们的创建顺序相反。全局和静态对象的销毁顺序与它们的定义顺序相反(但具体顺序在不同编译器和链接器之间可能有所不同)。

  5. 成员对象的析构:当一个对象被销毁时,它的成员对象(包括基类部分)的析构函数会按照它们被构造的相反顺序被调用。

 

需要自己实现析构函数的情况:

  •  如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数
  • 如果默认⽣成的析构就可以用,也就不需要显式写析构,比如类中的成员变量都为自定义类型,那么默认析构函数会自动调用成员变量的析构函数
  • 有资源申请时,⼀定要自己写析构,否则会造成资源泄漏

 

示例代码:

以下是一个简单的C++类,它包含动态分配的内存,并在析构函数中释放这些内存:

#include <iostream>  
  
class MyClass {  
private:  
    int* data; // 动态分配的内存  
  
public:  
    // 构造函数,动态分配内存  
    MyClass(int size) {  
        data = new int[size];  
        std::cout << "Memory allocated for MyClass object." << std::endl;  
    }  
  
    // 析构函数,释放动态分配的内存  
    ~MyClass() {  
        delete[] data;  
        std::cout << "Memory freed for MyClass object." << std::endl;  
    }  
  
    // 其他成员函数...  
};  
  
int main() {  
    MyClass obj(10); // 创建对象,分配内存  
    // ... 使用对象 ...  
    // 当obj离开作用域时,析构函数被调用,释放内存  
    return 0;  
}

 

实现析构函数需要考虑的问题:

析构函数的实现通常涉及以下步骤

  1. 释放动态内存如果对象在生命周期中动态分配了内存,析构函数应该负责释放这些内存
  2. 关闭文件和网络连接:如果对象持有文件句柄或网络连接,析构函数应该负责关闭它们。
  3. 调用成员对象的析构函数:如果对象包含其他对象作为成员,析构函数将自动调用这些成员对象的析构函数(按照成员变量在类中声明的逆序)。注意是自动调用,无需也无法主动调用
  4. 执行其他必要的清理操作:如解除锁、记录日志等。

 

 

结尾

C++析构函数是对象生命周期管理的重要组成部分。通过正确地实现和使用析构函数,程序员可以确保资源得到正确的释放和管理,从而避免内存泄漏和其他资源相关问题。

理解析构函数的工作原理和最佳实践对于编写健壮、可维护的C++代码至关重要。

 

系列后续文章:

【C++指南】类和对象(四):类的默认成员函数——全面剖析 拷贝构造函数-****博客

【C++指南】类和对象(五):类的默认成员函数——全面剖析 赋值运算符重载函数-****博客