📜  在C++中玩析构函数(1)

📅  最后修改于: 2023-12-03 15:23:22.688000             🧑  作者: Mango

在C++中玩析构函数

什么是析构函数

在C++中,析构函数是一种特殊的成员函数,负责在对象被销毁时执行清理工作。析构函数的名称与类名相同,但前面加上了一个波浪号(~)作为前缀。

析构函数通常用于释放在对象生命期内为其分配的内存、释放文件资源、关闭文件等。

析构函数的语法

析构函数的语法如下:

~ClassName() {
    // 析构函数的代码
}

在构造函数中使用了new运算符分配的内存,应该在析构函数中使用delete运算符进行释放操作。

析构函数的调用时机

析构函数调用的时机可以分为两种情况:对象生命周期结束和异常。

对象生命周期结束

当一个对象生命周期结束时,会自动调用其析构函数。如:

Class A {
public:
    A() { // 构造函数 }

    ~A() { // 析构函数 }
};

void function() {
    A obj; // 在函数内部创建 A 对象
} // 对象 obj 生命周期结束时调用析构函数
异常

如果在堆上分配了动态内存并进行了初始化,但由于某种原因导致程序抛出异常或被中断,这将导致程序无法正常结束,也就是说没有调用析构函数。为避免这种情况,可以利用常规的try-catch语句来确保在异常情况下释放由new操作符分配的动态内存。如:

Class A {
public:
    A() {
        try {
            // 在构造函数中申请动态内存
            ptr = new int[10];
        } catch (const std::exception& e) {
            std::cerr << "Constructor failed: " << e.what() << '\n';
        }
    }

    ~A() {
        // 在析构函数中释放动态内存
        delete[] ptr;
    }

private:
    int* ptr;
};
错误的析构函数
  • 构造函数中已经分配了资源,但析构函数中没有释放。
  • 析构函数中释放了内存,但是该内存已经在析构函数之前被释放了。
  • 将析构函数声明为虚函数,但是没有任何继承关系。
析构函数的应用场景
动态内存的管理

如果在类的构造函数中进行动态内存分配,那么在析构函数中释放动态内存是一个很好的选择。如:

class A {
public:
    A() {
        // 在构造函数中申请动态内存
        ptr = new int[10];
    }

    ~A() {
        // 在析构函数中释放动态内存
        delete[] ptr;
    }

private:
    int* ptr;
};
销毁资源

如果类需要与外部环境交互,如打开文件、打开数据库等,那么可以将连接和释放连接的操作放在构造函数和析构函数中。

class Database {
public:
    Database() {
        // 连接数据库
    }

    ~Database() {
        // 断开数据库连接
    }

    void query(...); // 查询操作
    void insert(...); // 插入操作

private:
    // 数据库连接相关的变量
};
总结
  • 析构函数是一个特殊的成员函数,负责在对象被销毁时执行清理工作。
  • 析构函数的名称与类名相同,但前面加上了一个波浪号(~)作为前缀。
  • 析构函数通常用于释放在对象生命期内为其分配的内存、释放文件资源、关闭文件等。
  • 析构函数在对象生命周期结束和异常时被调用。
  • 在析构函数中应该释放在构造函数中分配的动态内存。
  • 应该避免错误的析构函数:1. 构造函数中已经分配了资源,但析构函数中没有释放。2. 析构函数中释放了内存,但是该内存已经在析构函数之前被释放了。
  • 析构函数还可用于销毁资源,如关闭文件、断开数据库连接等。