C++面向对象程序设计
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

3.6 析构函数

析构函数(destructor)是与类同名的另一个特殊成员函数,作用与构造函数相反,用于在对象生存期结束时,完成对象的清理工作。如用delete删除对象分配的自由空间,清除某些内存单元的内容等。析构函数的名字由“~”+“类名”构成,形式如下:

class X{
   ……
  public:
   ~X();    //析构函数
   ……
};

在类外定义析构函数的形式如下:

X::~X(){
   ……
}

析构函数具有以下特点:析构函数的名字就是在类名前加上“~”,不能是其他名字;析构函数没有返回类型(void也不行),没有参数表;析构函数不能重载,一个类只能有一个析构函数;析构函数只能由系统自动调用,不能在程序中显式调用析构函数。

当创建一个对象时,C++将首先为数据成员分配存储空间,接着调用构造函数对成员进行初始化工作;当对象生存期结束时,C++将自动调用析构函数清理对象所占据的存储空间,然后才销毁对象。

例3-12】 析构函数和构造函数的应用。

//Eg3-12.cpp
#include <iostream>
using namespace std;
class A{
  private:
    int i;
  public:
    A(int x){
       i=x;
       cout<<"constructor: "<<i<<endl;
    }
    ~A(){  cout<<"destructor : "<<i<<endl; }
};
void main(){
    A a1(1);
    A a2(2);
    A a3(3);
}                          //L1

本程序的运行结果如下:

constructor: 1
constructor: 2
constructor: 3
destructor : 3
destructor : 2
destructor : 1

当程序执行到语句L1位置时,所有对象的生存期到此结束,将按照与构造相反的次序调用析构函数完成对象销毁前的清理工作,程序的运行结果也证实了这一情况。

说明:① 若有多个对象同时结束生存期,C++将按照与调用构造函数相反的次序调用析构函数。

② 构造函数和析构函数都可以是inline()函数。

③ 在通常情况下,析构函数与构造函数都应该被设置为类的公有成员,虽然它们都只能被系统自动调用,但这些调用都是在类的外部进行的。

④ 每个类都应该有一个析构函数。如果没有显式定义析构函数,C++编译器将产生一个最小化的默认析构函数,类似下面的情况:

X::~X(){ }

在多数情况下,默认析构函数都能够满足对象析构的要求。但在有些情况下,需要编写析构函数完成对象销毁前的资源清理工作,最常见的是用来释放由构造函数分配的自由存储空间。

例3-13】 用析构函数释放构造函数分配的自由存储空间。

//Eg3-13.cpp
#include <iostream>
using namespace std;
class B{
  private:
    int *a;
    char *pc;
  public:
  inline B(int x){
     a=new int[10];
     pc=new char;
  }
  inline ~B(){
     delete []a;
     delete pc;
  }
};
void main(){
  B x(1);
}

对于例3-13而言,如果没有析构函数,程序也能正常地编译运行。但是,当用类B建立的对象结束生存期时,系统不会回收由B的构造函数分配的自由存储空间,会产生内存泄漏。

例3-13就是应该为类提供析构函数的典型情况,即若在构造函数中用new或malloc分配了存储空间,就应该在析构函数中用delete或free释放这些存储空间。