在学习链表的时候,我们知道结点是动态生成的,如果在程序结束之前不释放内存,就会造成内存泄漏。虽然我们已经编写了成员函数Destroy来删除所有动态生成的结点,但是如果我们不知道这个链表对象何时不再使用,那么调用Destroy的时机对我们来说就是个麻烦了。如果过早地调用,则后面的程序可能会出错。既然有构造函数能随着对象的创建而自动被调用,那么有没有一种函数能随着对象的消亡而自动被调用呢?有!那就是析构函数(Destructor)。
析构函数是一种随着对象消亡而自动被调用的函数,它的主要用途是释放动态申请的资源。它没有返回类型,没有参数,也没有重载。析构函数的函数名也是指定的,是在构造函数名之前加一个“~”符号。
下面我们为程序15.4.2添上析构函数的功能:(程序15.5)
//node.h
#include <iostream>
using namespace std;
class Node//定义一个链表结点类
{
public:
Node();//构造函数的声明
Node(Node &n);//结点拷贝构造函数
Node(int i,char c='0');//构造函数重载1
Node(int i,char c,Node *p,Node *n);//构造函数重载2
~Node();//结点析构函数
int readi() const;//读取idata
char readc() const;//读取cdata
Node * readp() const;//读取上一个结点的位置
Node * readn() const;//读取下一个结点的位置
bool set(int i);//重载,通过该函数修改idata
bool set(char c);//重载,通过该函数修改cdata
bool setp(Node *p);//通过该函数设置前驱结点
bool setn(Node *n);//通过该函数设置后继结点
private:
int idata;//存储数据保密
char cdata;//存储数据保密
Node *prior;//前驱结点的存储位置保密
Node *next;//后继结点的存储位置保密
};
//未定义的函数与程序15.4.1相同
Node::~Node()
{
cout <<"Node destructor is running..." <<endl;
}
//linklist.h
#include "node.h"//需要使用链表结点类
#include <iostream>
using namespace std;
class Linklist
{
public:
Linklist(int i,char c);//链表类构造函数
Linklist(Linklist &l);//链表深拷贝构造函数
~Linklist();//链表析构函数
bool Locate(int i);//根据整数查找结点
bool Locate(char c);//根据字符查找结点
bool Insert(int i=0,char c='0');//在当前结点之后插入结点
bool Delete();//删除当前结点
void Show();//显示链表所有数据
void Destroy();//清除整个链表
private:
Node head;//头结点
Node * pcurrent;//当前结点指针
};
//未定义的函数与程序15.4.2相同
Linklist::~Linklist()
{
cout<<"Linklist destructor is running..."<<endl;
Destroy();//一个成员函数调用另一个成员函数不需要带上对象名
}
//main.cpp 同程序15.4.2
运行结果:
请输入一个整数和一个字符:
4 G
Node constructor is running...
Linklist constructor is running...
Node constructor is running...
Node constructor is running...
Node constructor is running...
After Insert
4 G
3 F
2 B
1 C
Node destructor is running...
After Delete
4 G
3 F
1 C
Linklist Deep cloner running...
Node constructor is running...
Node constructor is running...
This is Linklist b
4 G
3 F
1 C
Node destructor is running...
Node destructor is running...
After Destroy
4 G
This is Linklist b
4 G
3 F
1 C
Linklist destructor is running...
Node destructor is running...
Node destructor is running...
Node destructor is running...
Linklist destructor is running...
Node destructor is running...
在After Destroy之前的两条Node destructor运行是因为调用了a.Destroy(),最后的6条destructor是因为程序运行结束使得对象自动消亡。可见析构函数是在使用delete语句删除动态生成的对象或程序结束对象消亡时自动被调用的。
从最后的2条destructor输出我们发现,当一个对象的成员数据还是对象时,析构函数的运行顺序恰好与构造函数的运行顺序相反:一个大对象先调用析构函数,瓦解成若干成员数据,然后各个成员数据再调用各自的析构函数。这体现出构造函数与析构函数的对称性。