派生类的析构函数也是在派生类对象释放的时候进行清理工作的。前面说过,派生类无法继承基类的析构函数,所以如果需要的话就要自己定义析构函数。派生类析构函数的定义方式与一般类的析构函数是一样的,也是没有返回类型,没有参数,所以比构造函数需要注意的东西少多了。
派生类的析构函数一般只需要在其函数体中清理新增成员就可以了,对于继承的基类成员和派生类内嵌对象成员的清理,则一般由系统自动调用基类和对象成员的析构函数来完成。这个执行过程的顺序正好和派生类构造函数相反:1.执行析构函数语句清理派生类的新增成员;2.调用内嵌对象成员所属类的析构函数清理派生类内嵌对象成员,各个对象成员的清理顺序与其在构造函数中的构造顺序相反;3.调用基类的析构函数清理继承的基类成员,如果是多继承则各个基类的清理顺序也与其在构造函数中的构造顺序相反。总起来一句话,析构函数执行时所有成员或对象的清理顺序与构造函数的构造顺序刚好完全相反。
前面的例程中所有类都没有定义析构函数,这时系统会为每个类生成默认析构函数,由他们完成清理工作。
讲前面的例子稍加改造,为每个基类显示定义析构函数,然后看下程序的执行顺序。
- #include <iostream>
- using namespace std;
- class Base1 // 基类Base1
- {
- public:
- Base1() { cout<<"Base1 construct"<<endl; } // Base1的构造函数
- ~Base1() { cout<<"Base1 destruct"<<endl; } // Base1的析构函数
- };
- class Base2 // 基类Base2
- {
- public:
- Base2(int x) { cout<<"Base2 construct "<<x<<endl; } // Base2的构造函数
- ~Base2() { cout<<"Base2 destruct"<<endl; } // Base2的析构函数
- };
- class Base3 // 基类Base3
- {
- public:
- Base3(int y) { cout<<"Base3 construct "<<y<<endl; } // Base3的构造函数
- ~Base3() { cout<<"Base3 destruct"<<endl;} // Base3的析构函数
- };
- class Child : public Base2, public Base1, public Base3 // 派生类Child
- {
- public:
- Child(int i,int j,int k,int m):Base2(i),b3(j),b2(k),Base3(m) { }
- private: // 派生类的内嵌对象成员
- Base1 b1;
- Base2 b2;
- Base3 b3;
- };
- int main()
- {
- Child child(3,4,5,6);
- return 0;
- }
三个基类Base1、Base2和Base3都添加了析构函数,派生类Child没有添加,系统会为派生类生成默认析构函数。主函数的函数体没有变。程序执行时会先调用Child类的构造函数构造child对象,然后调用Child类的默认析构函数完成清理工作。Child类的默认析构函数会依次调用内嵌对象成员的析构函数和基类的析构函数,执行顺序和构造函数完全相反。
上面程序的运行结果是:
Base2 construct 3
Base1 construct
Base3 construct 6
Base1 construct
Base2 construct 5
Base3 construct 4
Base3 destruct
Base2 destruct
Base1 destruct
Base3 destruct
Base1 destruct
Base2 destruct
从程序的执行结果来看,和前面的分析完全一致,析构函数的执行顺序与构造函数完全相反。