析构函数(distructor)是一种特殊的成员函数,与构造函数相反,名字在类名前面加一个“~”符号。对象生命周期结束时,会自动执行析构函数。有如下情况:
<1> 如果在一个函数中定义一个对象,当这个函数被调用结束时,对象应该释放,在对象释放前自动执行析构函数。
<2> static局部对象在函数调用结束时,对象并不释放,因此也不调用析构函数,只在main函数结束或者exit函数结束程序时,才调用static局部对象的析构函数。
<3> 如果定义了一个全局对象,则在程序的流程离开作用域时,才调用该对象的析构函数。
<4> 如果用new运算符动态的建立一个对象,当用delete运算符释放该对象时,先调用对象的析构函数。
析构函数的作用,并不是删除对象,而是在撤销对象占用的内存之前完成一些清理工作,使这部分内存可以被程序分配给新对象使用。析构函数不返回任何值,没有函数类型,也没有函数参数。由于没有函数参数,所以不能被重载,一个类可以有多个构造函数,但只能用一个析构函数。
实际上,析构函数的作用并不仅限于释放资源方面,它还可以被用来执行“用户希望在最后一次使用对象之后所执行的任何操作”,例如输出有关信息。
析构函数,是在声明类的时候定义的。也就是说可以完成类的设计者所指定的任何操作。一般情况下,类的设计者应当声明类的同时定义析构函数,指定“清理工作”。如果没有显性定义的话,那么编译系统会自动生成一个析构函数,但它徒有析构函数的名称和形式,不执行任何操作。
#include <string>
#include <iostream>
using namespace std;
class Student
{
public:
Student(int n, string nam, char s) //constructor
{
num = n;
name = nam;
sex = s;
cout << "Constructor called." << endl;
}
~Student() //destructor
{
cout << "Destructor called" << endl;
}
void display()
{
cout << "num: " << num << endl;
cout << "name: " << name << endl;
cout << "sex: " << sex << endl <<endl;
}
private: //private data members, unaccessable to public
int num;
string name;
char sex;
};
int main()
{
Student stud1(10010, "Wang_li",'f');
stud1.display();
Student stud2(10011, "Zhang_fun",'m');
stud2.display();
return 0;
}
在main函数前面声明类,它的作用域是全局的。这样可以使main函数更加简练。在student类中定义了构造函数和析构函数。在执行main函数时先建立对象stud1,同时调用构造函数,给该对象中的数据成员赋初值,然后执行dispaly函数。同样的执行对象stud2。在执行return语句后,主函数的语句已经执行完毕,对主函数的调用结束了。在主函数中建立对象是局部的,生命周期随着主函数的结束而结束,在撤销对象之前的最后一项工作是调用析构函数。这里的析构函数并无实质作用,只是输出一个信息。
Constructor called.
num = 10010
name = Wang_li
sex = f
Constructor called.
num = 10011
name = Zhang_fun
sex = m
Destructor called
Destructor called
Program ended with exit code: 0
细心的人会发现,为什么会有两个“Destructor called”的?这个不难解释,因为main函数中定义了两个对象。那么进一步思考,这两个的顺序是怎么样的?哪个是stud1哪个是stud2?
这里有一个顺序:“先构造的后析构,后构造的先析构”。
验证一下:在Student类的析构函数改写成:
cout << "Destructor called" << num <<endl;
输出结果为:
Constructor called.
num = 10010
name = Wang_li
sex = f
Constructor called.
num = 10011
name = Zhang_fun
sex = m
Destructor called 10011
Destructor called 10010
Program ended with exit code: 0
验证了“先构造的后析构,后构造的先析构”。有个前提,这是对同一类存储的对象而言。但是并不都是按照这个原则处理的。作用域和存储类别会对这个处理原则产生影响的。例如:如果在函数中定义静态(static)局部对象,则只在程序第一次调用此函数时调用一次构造函数,在调用结束时,对象并不释放,因此也不会调用析构函数,只在main函数结束或者调用exit函数结束程序时,才调用析构函数。
void fn()
{
Student stud1; //定义自动局部对象
static Student stud2; //定义静态对象
.
.
.
}
在调用fn函数时,先调用stud1的构造函数,在调用stud2的构造函数,在fn调用结束时,stud1是要释放的(因为它是自动局部对象),因此调用析构函数。而stud2是静态局部对象,在fn调用结束时并不释放,因此不调用析构函数。直到程序结束释放stud12,才会调用析构函数。