# 析构函数 创建对象时系统会自动调用构造函数进行初始化工作,同样,销毁对象时系统也会自动调用一个函数来进行清理工作,例如释放分配的内存、关闭打开的文件等,这个函数就是析构函数。 析构函数(Destructor)也是一种特殊的成员函数,没有返回值,不需要程序员显式调用(程序员也没法显式调用),而是在销毁对象时自动执行。构造函数的名字和类名相同,而析构函数的名字是在类名前面加一个`~`符号。 注意:析构函数没有参数,不能被重载,因此一个类只能有一个析构函数。如果用户没有定义,编译器会自动生成一个默认的析构函数。 上节我们定义了一个 VLA 类来模拟变长数组,它使用一个构造函数为数组分配内存,这些内存在数组被销毁后不会自动释放,所以非常有必要再添加一个析构函数,专门用来释放已经分配的内存。请看下面的完整示例: ```c++ #include using namespace std; class VLA { public: VLA(int len); //构造函数 ~VLA(); //析构函数 void input(); void show(); private: const int m_len; int *m_arr; int *m_p; int *at(int i); }; VLA::VLA(int len) : m_len(len) { if (len > 0) { m_arr = new int[len]; } else { m_arr = NULL; } } VLA::~VLA() { delete[] m_arr; } void VLA::input() { for (int i = 0; m_p = at(i); i++) { cin >> *at(i); } } void VLA::show() { for (int i = 0; m_p = at(i); i++) { if (i == m_len - 1) { cout << *at(i) << endl; } else { cout << *at(i) << ", "; } } } int *VLA::at(int i) { if (!m_arr || i < 0 || i >= m_len) { return NULL; } else { return m_arr + i; } } int main() { //创建一个有n个元素的数组(对象) int n; cout << "Input array length: "; cin >> n; VLA *parr = new VLA(n); //输入数组元素 cout << "Input " << n << " numbers: "; parr->input(); //输出数组元素 cout << "Elements: "; parr->show(); //删除数组(对象) delete parr; return 0; } ``` `~VLA()`就是 VLA 类的析构函数,它的唯一作用就是在删除对象(第 53 行代码)后释放已经分配的内存。 函数名是标识符的一种,原则上标识符的命名中不允许出现`~`符号,在析构函数的名字中出现的`~`可以认为是一种特殊情况,目的是为了和构造函数的名字加以对比和区分。 注意:at() 函数只在类的内部使用,所以将它声明为 private 属性;m_len 变量不允许修改,所以用 const 进行了限制,这样就只能使用初始化来进行赋值。 C++ 中的 new 和 delete 分别用来分配和释放内存,它们与C语言中 malloc()、free() 最大的一个不同之处在于:用 new 分配内存时会调用构造函数,用 delete 释放内存时会调用析构函数。构造函数和析构函数对于类来说是不可或缺的,所以在C++中我们非常鼓励使用 new 和 delete。 ## 析构函数的执行时机 析构函数在对象被销毁时调用,而对象的销毁时机与它所在的内存区域有关。 在所有函数之外创建的对象是全局对象,它和全局变量类似,位于内存分区中的全局数据区,程序在结束执行时会调用这些对象的析构函数。 在函数内部创建的对象是局部对象,它和局部变量类似,位于栈区,函数执行结束时会调用这些对象的析构函数。 new 创建的对象位于堆区,通过 delete 删除时才会调用析构函数;如果没有 delete,析构函数就不会被执行。