在学习链表的时候,我们知道结点是动态生成的,如果在程序结束之前不释放内存,就会造成内存泄漏。虽然我们已经编写了成员函数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输出我们发现,当一个对象的成员数据还是对象时,析构函数的运行顺序恰好与构造函数的运行顺序相反:一个大对象先调用析构函数,瓦解成若干成员数据,然后各个成员数据再调用各自的析构函数。这体现出构造函数与析构函数的对称性。