一般我们都知道构造和析构的次序:
构造从类层次的最根处开始,在每一层中,首先调用基类的构造函数,然后调用成员对象的构造函数。析构则严格按照与构造相反的次序执行,该次序是唯一的,否则编译器将无法自动执行析构过程。
构造函数和析构函数都是由编译器隐式调用的。这些函数的调用顺序取决于程序的执行进入和离开实例化对象时所在的那个作用域的顺序。一般而言,析构函数的调用顺序和构造函数的调用顺序相反,但是,对象的存储类可以改变析构函数的调用顺序。
- 对于在全局作用域中定义的对象,它们的构造函数是在文件中所有其他函数(包括main)开始执行之前被调用的(但无法保证不同文件的全局对象构造函数的执行顺序)。对应的析构函数是在终止main之后调用的
- exit函数会迫使程序立即终止,而不会执行自动对象的析构函数。这个函数经常用来在检测到输入错误或者程序所处理的文件无法打开时终止程序。
- abort函数与exit函数功能相似,但它会迫使程序立即终止,而不允许调用任何对象的析构函数。abort函数通常用来表明程序的非正常终止。
- 自动局部变量的构造函数是在程序的执行到达定义这个对象的位置时调用的,而对应的析构函数是在程序离开这个对象的作用域时调用的(即定义这个对象的代码完成了执行)。每次执行进入和离开自动对象的作用域时,都会调用它的构造函数和析构函数。如果程序调用了exit或abort函数而终止,则不会调用自动对象的析构函数。
- 静态局部对象的析构函数只调用一次,即执行首次到达定义这个对象的位置时。对应的析构函数是在main终止或程序调用exit函数时调用的。
- 全局对象和静态对象是以创建它们时相反的顺序销毁的。如果程序由于调用了exit函数而终止,则不会调用静态对象的析构函数。
析构函数在下边3种情况时被调用:
- 对象生命周期结束,被销毁时;
- delete指向对象的指针时,或delete指向对象的基类类型指针,而其基类虚构函数是虚函数时;
- 对象i是对象o的成员,o的析构函数被调用时,对象i的析构函数也被调用。
1、例1
#include<iostream.h>
class A{
public:
A(){
cout<<"constructing A"<<endl;
}
~A(){
cout<<"destructing A"<<endl;
}
private:
int a;
};
class B: public A{
public:
B(){
cout<<"constructing B"<<endl;
}
~B(){
cout<<"destructing B"<<endl;
}
private:
int b;
};
void main(){
B b;
}
运行结果为:
constructing A
constructing B
destructing B
destructing A
上述代码还说明了一件事:析构函数的调用顺序与构造函数的调用顺序相反。
2、例2
例子2则正好说明了为什么基类应该把析构函数声明为虚函数,请先看下边的例子:
#include<iostream.h>
class A{
public:
A(){
cout<<"constructing A"<<endl;
}
~A(){
cout<<"destructing A"<<endl;
}
private:
int a;
};
class B: public A{
public:
B(){
cout<<"constructing B"<<endl;
}
~B(){
cout<<"destructing B"<<endl;
}
private:
int b;
};
void main(){
A* a = new B;
delete a;
}
运行结果为:
constructing A
constructing B
destructing A
若将class A中的析构函数声明为虚函数,运行结果将变成:
constructing A
constructing B
destructing B
destructing A
3、例3
#include<iostream.h>
class A{
public:
A(){
cout<<"constructing A"<<endl;
}
~A(){
cout<<"destructing A"<<endl;
}
private:
int a;
};
class C{
public:
C(){
cout<<"constructing C"<<endl;
}
~C(){
cout<<"destructing C"<<endl;
}
private:
int c;
};
class B:public A{
public:
B(){
cout<<"constructing B"<<endl;
}
~B(){
cout<<"destructing B"<<endl;
}
private:
int b;
C c;
};
void main()
{
B b;
}
运行结果为:
constructing A
constructing C
constructing B
destructing B
destructing C
destructing A
b的析构函数调用之后,又调用了b的成员c的析构函数,同时再次验证了析构函数的调用顺序与构造函数的调用顺序相反。
若将上边的代码中的main()函数内容改成
A* a = new B;
delete a;
由例2我们知道,这样class B的析构函数不会被调用,所以class C的析构函数也不会被调用。
正如我们想的,运行结果为:
constructing A
constructing C
constructing B
destructing A
原文链接: