析构函数
一.函数原型
•析构函数是特殊的成员函数
–析构函数的函数名就是在类名前面加“~”
–析构函数没有返回类型
–析构函数没有参数
–析构函数不能重载
class Array
{
public:
~Array (void) { ... }
};
二.谁来调用
•析构函数在销毁对象时自动被调用
–对象所在作用域的终止花括号调用析构函数
{
Array array (10);
…
}
–delete操作符调用析构函数
Array* array = new Array (10);
…
delete array;
•析构函数在对象的整个生命周期内,最多被调用一次
三.释放资源
•析构函数负责释放在对象的构造过程或生命周期内所获得的资源
–class Array
{
public: Array (size_t size) : m_array (new int[size]) {}
~Array (void)
{
delete[] m_array;
}
private:
int* m_array;
};
善后事宜
四.善后事宜
•析构函数的功能不仅限于释放资源,它可以执行任何作为类的设计者希望在最后一次使用对象之后执行的动作
–class Child
{
public:
Child (Parent* parent) : m_parent (parent)
{
m_parent->register (this);
}
~Child (void)
{
m_parent->unregister (this);
}
private:
Parent* m_parent;
};
五.缺省析构函数
•通常情况下,如果对象在其生命周期的最终时刻,并不持有任何动态分配的资源,也没有任何善后工作可做,那么完全可以不为其定义析构函数
•如果一个类没有定义析构函数,那么编译器会为其提供一个缺省析构函数
–对基本类型的成员变量,什么也不做
–对类类型的成员变量和基类子对象,调用相应类型的析构函数
•缺省析构函数由编译器提供,它只负责释放编译器看得到资源,如成员子对象、基类子对象等
•对于编译器看不到的资源,如通过malloc或new动态分配的资源,缺省析构函数不负责释放,必须通过自己定义的析构函数予以释放,否则将形成内存泄漏
–class User
{
User (string const& name) : m_name (new string (name)),
m_info (static_cast<char*> (malloc (1024))) {}
~User (void)
{
delete m_name;
free (m_info);
}
string* m_name;
char* m_info;
};
对象的销毁过程
一.调用析构函数
•销毁对象的第一步即调用析构函数,完成如下任务
1.执行析构函数体代码
2.逆序调用类类型成员的析构函数,析构所有成员子对象
3.逆序调用各个基类的析构函数,析构所有基类子对象
•注意,执行析构函数体代码是整个析构过程的第一步,这保证了析构函数体代码所依赖的一切资源和先决条件,
在该代码被执行时尚且未被销毁,并保持析构前的状态
二.释放内存空间
•销毁对象的第二步是用类似free的机制,将包括成员子对象、基类子对象等在内的,整个对象内存空间释放掉
•根据目前绝大多数C++编译器的实现,销毁对象的过程和创建对象的过程,通常是严格相反的
–创建:分配内存->构造基类->构造成员->执行构造代码
基类:按继承顺序,从左至右,依次构造
成员:按声明顺序,从上至下,依次构造
–销毁:执行析构代码->析构成员->析构基类->释放内存
基类:按继承顺序,从右至左,依次析构
成员:按声明顺序,从下至上,依次析构