析构函数
前面介绍的构造函数用于在创建对象时完成数据成员的初始化,与之对应,对象生命期结束前应该完成对象资源的清理,这个工作由析构函数完成。比如创建对象时为数据成员开辟的空间,会通过析构函数在对象的生命期结束前进行释放。
析构函数是类中特殊的成员函数,用于完成资源清理,析构函数的定义形式如下所示:
类名::~析构函数()
{
函数体
}
定义析构函数应满足以下要求:
● 析构函数的名称是在构造函数名称之前添加“~”。
● 析构函数没有参数。
● 析构函数中不能通过return语句返回一个值。
● 一个类中只能有一个析构函数,不可重载。
下面在类Car中添加析构函数,代码如下所示:
class Car
{
public:
Car() //构造函数
{
m_strCarName = "car name";
m_nSeats = 4;
}
~Car() //析构函数
{
}
private:
string m_strCarName;
int m_nSeats;
};
上述代码中的析构函数没有做任何工作。与构造函数类似,若类中没有显式定义析构函数,则编译器会给出一个默认的析构函数,该函数没有具体的动作,在对象生命期结束时默认的析构函数被执行。
接下来通过一个案例来说明如何通过析构函数释放对象资源,如例1所示。
例1
1 #include <iostream>
2 #include <cstring> //strcpy_s()函数声明所在的头文件
3 using namespace std;
4
5 class Car //定义类
6 {
7 public:
8 Car(); //无参构造函数的声明
9 Car(char *con_pcarname, int con_seats); //带参构造函数的声明
10 ~Car(); //析构函数的声明
11 char *get_carname();
12 int get_seats();
13 private:
14 char *m_pCarName;
15 int m_nSeats;
16 };
17 Car::Car() //定义无参的构造函数
18 {
19 cout << "Car constructor!" << endl;
20 m_pCarName = nullptr; //设置指针成员的初值为nullptr,值为0
21 m_nSeats = 4;
22 }
23 Car::Car(char *con_pcarname, int con_seats) //定义带参数的构造函数
24 {
25 int len = strlen(con_pcarname) + 1;
26
27 cout << "Car constructor with param,car name:" << con_pcarname <<endl;
28 m_pCarName = new char[len]; //数据成员m_pCarName指向新开辟的空间
29 strcpy_s(m_pCarName, len, con_pcarname); //将汽车名车存入m_pCarName指向的空间
30 m_nSeats = con_seats;
31 }
32 Car::~Car() //定义析构函数
33 {
34 static int i = 0;
35 cout << "Car destructor,car name:" << m_pCarName << endl;
36 if (m_pCarName)
37 delete[] m_pCarName; //释放m_pCarName指向的空间
38 if (i == 1)
39 system("pause");
40 i++;
41 }
42 char *Car::get_carname() //获取m_pCarName属性值
43 {
44 return m_pCarName;
45 }
46 int Car::get_seats() //获取m_nSeats属性值
47 {
48 return m_nSeats;
49 }
50 int main()
51 {
52 Car mynewcar("my car", 4); //创建对象
53 Car tomcar("tom car", 7); //创建对象
54
55 cout << "my car name : " << mynewcar.get_carname() << endl;
56 cout << "tom car name: " << tomcar.get_carname() << endl;
57
58 return 0;
59 }
运行结果如图1所示。
图1 例2-10运行结果
例1中类Car内定义了指针数据成员m_pCarName,用来记录存有汽车名称空间的地址,通过构造函数Car(char *con_pcarname, int con_seats)对m_pCarName进行初始化时,应通过new开辟一段空间存放汽车名称,空间地址记录在m_pCarName中。m_pCarName与存放汽车名称空间的关系如图2所示。
图2 m_pCarName指向开辟的空间
例1构造函数中会通过new开辟空间,new应该与delete严格匹配,因此在对象的生命期结束前应该通过delete释放空间,析构函数能够完成这样的工作。代码32-41行是析构函数,第36行测试m_pCarName指针是否有效,若有效则代表m_pCarName指向一段new的空间,应通过delete释放。从运行结果看出,对象生命期结束时析构函数被自动调用,完成了资源清理工作。
另外,运行结果还显示,若定义了多个类对象:mynewcar、tomcar,析构函数的调用顺序和构造函数的调用顺序相反,后创建的对象先析构,先创建的对象后析构。