-
析构函数是一个成员函数,该函数在对象超出范围时自动调用,或者通过delete显式调用。
-
如果未定义析构函数,则编译器将提供一个默认析构函数。对于许多类,这就足够了。仅当类将句柄存储到需要释放的系统资源或拥有它们指向的内存的指针时,才需要定义自定义析构函数。
#include <stdlib.h>
#include <string.h>
using namespace std;
class String {
public:
String( char *ch ); //声明构造函数
~String(); //声明析构函数
private:
char *_text;
size_t sizeOfText;
};
//定义构造函数。
String::String( char *ch ) {
sizeOfText = strlen( ch ) + 1;
//动态分配正确数量的内存。
_text = new char[ sizeOfText ];
//如果分配成功,复制初始化字符串。
if( _text )
strcpy_s( _text, sizeOfText, ch );
}
// 定义析构函数。
String::~String() {
// 释放以前为此字符串保留的内存。
delete[] _text;
}
int main() {
String str("The piper in the glen...");
}
声明析构函数
- 析构函数具有与类相同的名称,前面有一个波形符 (~) 。 例如,声明 String 类的析构函数:
~String()。
多个规则管理析构函数的声明。 析构函数:
- 不接受自变量
- 不要将值返回 (或 void) 。
- 不能声明为 const 、 volatile 或 static 。 但是,可以调用它们来销毁作为、或声明的对象
const volatile static
。 - 可以声明未
virtual
。通过使用虚拟析构函数,无需知道对象的类型即可销毁对象:使用虚函数机制调用该对象的正确析构函数。请注意,析构函数也可以声明为抽象类的纯虚函数。
使用构造函数
当下列事件之一发生时,将调用析构函数:
- 具有块范围的本地(自动)对象超出范围。
- 使用运算符分配的对象
new
使用进行了显式释放delete
。 - 临时对象的生存期结束
- 程序结束,并且存在全局或者静态对象
- 使用析构函数的完全限定名显式调用了析构函数
析构函数可以随意调用类成员函数和访问类数据
对析构函数的使用有两个限制:
- 不能采用其地址
- 派生类不继承其基类的析构函数
析构的顺序
当对象超出范围或者被删除时,其完整析构中的事件序列如下所示:
- 将调用该类的析构函数,并且会执行该析构函数的主体
- 按照非静态成员对象的析构函数在类声明中的显式顺序的相反顺序调用这些函数。构造这些成员时使用可选成员初始化列表不会影响构造或者析构的顺序
- 非虚拟基类的析构函数以相反顺序调用
- 虚拟基类的析构函数以声明的相反顺序被调用
#include <cstdio>
struct A1 {
virtual ~A1() {
printf("A1 dtor\n"); } };
struct A2 : A1 {
virtual ~A2() {
printf("A2 dtor\n"); } };
struct A3 : A2 {
virtual ~A3() {
printf("A3 dtor\n"); } };
struct B1 {
~B1() {
printf("B1 dtor\n"); } };
struct B2 : B1 {
~B2() {
printf("B2 dtor\n"); } };
struct B3 : B2 {
~B3() {
printf("B3 dtor\n"); } };
int main() {
A1 * a = new A3;
delete a;
printf("\n");
B1 * b = new B3;
delete b;
printf("\n");
B3 * b2 = new B3;
delete b2;
}
非虚拟基类
非虚拟基类的析构函数以声明基类名称的反向顺序调用。 考虑下列类声明:
class MultInherit : public Base1, public Base2
...
在前面的示例中,先于 Base2 , 再调用 Base1 的析构函数。
显式析构函数调用
很少需要显式调用析构函数。但是,对置于绝对地址的对象进行清理会很有用。这些对象通常采用位置参数的用户定义new
运算符进行分配。delete
无法释放此内存,因为它不是从免费存储分配。但是,对析构函数的调用可以执行相应的清理。如果要显式调用s
类的对象string
的析构函数,请使用下列语句之一:
s.String::~String(); // non-virtual call
ps->String::~String(); // non-virtual call
s.~String(); // Virtual call
ps->~String(); // Virtual call