C++析构函数详解
析构函数(destructor)是成员函数的一种,它的名字与类名相同,但前面要加
一个类有且仅有一个析构函数。如果定义类时没写析构函数,则编译器生成默认析构函数。如果定义了析构函数,则编译器不生成默认析构函数。
析构函数在对象消亡时即自动被调用。可以定义析构函数在对象消亡前做善后工作。例如,对象如果在生存期间用 new 运算符动态分配了内存,则在各处写 delete 语句以确保程序的每条执行路径都能释放这片内存是比较麻烦的事情。有了析构函数,只要在析构函数中调用 delete 语句,就能确保对象运行中用 new 运算符分配的空间在对象消亡时被释放。例如下面的程序:
只要对象消亡,就会引发析构函数的调用。下面的程序说明了析构函数起作用的一些情况。
Destructor called
-----------------------
Destructor called
Destructor called
Main ends.
Destructor called
Destructor called
第一次析构函数调用发生在第 13 行,delete 语句使得第 12 行动态分配的 CDemo 对象消亡。
接下来的两次析构函数调用发生在第 16 行,delete 语句释放了第 15 行动态分配的数组,那个数组中有两个 CDemo 对象消亡。最后两次析构函数调用发生在 main 函数结束时,因第 11 行的局部数组变量 array 中的两个元素消亡而引发。
函数的参数对象以及作为函数返回值的对象,在消亡时也会引发析构函数调用。例如:
func
destructor
test
destructor
after test
destructor
destructor
程序共输出 destructor 四次:
~
,没有参数和返回值。一个类有且仅有一个析构函数。如果定义类时没写析构函数,则编译器生成默认析构函数。如果定义了析构函数,则编译器不生成默认析构函数。
析构函数在对象消亡时即自动被调用。可以定义析构函数在对象消亡前做善后工作。例如,对象如果在生存期间用 new 运算符动态分配了内存,则在各处写 delete 语句以确保程序的每条执行路径都能释放这片内存是比较麻烦的事情。有了析构函数,只要在析构函数中调用 delete 语句,就能确保对象运行中用 new 运算符分配的空间在对象消亡时被释放。例如下面的程序:
class String{ private: char* p; public: String(int n); ~String(); }; String::~String(){ delete[] p; } String::String(int n){ p = new char[n]; }String 类的成员变量 p 指向动态分配的一片存储空间,用于存放字符串。动态内存分配在构造函数中进行,而空间的释放在析构函数 ~String() 中进行。这样,在其他地方就不用考虑释放空间的事情了。
只要对象消亡,就会引发析构函数的调用。下面的程序说明了析构函数起作用的一些情况。
#include<iostream> using namespace std; class CDemo { public: ~CDemo() { //析构函数 cout << "Destructor called"<<endl; } }; int main() { CDemo array[2]; //构造函数调用2次 CDemo* pTest = new CDemo; //构造函数调用 delete pTest; //析构函数调用 cout << "-----------------------" << endl; pTest = new CDemo[2]; //构造函数调用2次 delete[] pTest; //析构函数调用2次 cout << "Main ends." << endl; return 0; }程序的输出结果是:
Destructor called
-----------------------
Destructor called
Destructor called
Main ends.
Destructor called
Destructor called
第一次析构函数调用发生在第 13 行,delete 语句使得第 12 行动态分配的 CDemo 对象消亡。
接下来的两次析构函数调用发生在第 16 行,delete 语句释放了第 15 行动态分配的数组,那个数组中有两个 CDemo 对象消亡。最后两次析构函数调用发生在 main 函数结束时,因第 11 行的局部数组变量 array 中的两个元素消亡而引发。
函数的参数对象以及作为函数返回值的对象,在消亡时也会引发析构函数调用。例如:
#include <iostream> using namespace std; class CDemo { public: ~CDemo() { cout << "destructor" << endl; } }; void Func(CDemo obj) { cout << "func" << endl; } CDemo d1; CDemo Test() { cout << "test" << endl; return d1; } int main() { CDemo d2; Func(d2); Test(); cout << "after test" << endl; return 0; }程序的输出结果是:
func
destructor
test
destructor
after test
destructor
destructor
程序共输出 destructor 四次:
- 第一次是由于 Func 函数结束时,参数对象 obj 消亡导致的。
- 第二次是因为:第 20 行调用 Test 函数,Test 函数的返回值是一个临时对象,该临时对象在函数调用所在的语句结束时就消亡了,因此引发析构函数调用。
- 第三次是 main 函数结束时 d2 消亡导致的。
- 第四次是整个程序结束时全局对象 d1 消亡导致的。