对于一个类
class BaseClass
{
public:
BaseClass(char*, int);
~BaseClass();
private:
char* pstring; // 使用灵巧指针可能更好些
int number;
};
BaseClass::BaseClass(char* p, int num) : pstring(p), number(num)
{}
BaseClass::~BaseClass()
{
delete pstring; // 如果使用灵巧指针这里不需要
}
构造函数一般是在生成时期进行一些必要的初始化工作,析构函数则是在其生存周期结束后进行一些扫尾任务。
对于使用成员初始化列表进行初始化的构造函数(只要可能,这也是被鼓励的方式),其成员初始化列表中的成员顺序应该与类声明中的顺序保持一致,原因在于初始化的顺序是按照类声明中的顺序进行的,如果不是按照类声明中成员顺序进行初始化,很可能产生一些你不希望发生,且难以进行错误定位的问题。
在该类中,我们使用了普通指针,而不是灵巧指针,事实上这有可能给我们制造很多令人头疼的麻烦,解决的一个比较好的办事是通过灵巧指针予以替代。类似于这种用法:
auto_ptr<char> pstring;
构造函数即变更为:
BaseClass::BaseClass(auto_ptr<char> p, int num) : pstring(p), number(num)
{}
析构函数变更为:
BaseClass::~BaseClass()
{
// 原来的代码没有了:)
}
析构函数需要在对象生存周期结束前进行扫尾工作,在设计析构函数的过程中应该避免(更准确的说是禁止)异常传递到析构函数外部。
析构函数在两种情况下被调用,一是正常删除对象,如生存周期结束前,调用delete operator 删除等。第二种情况是异常传递的堆栈展开(stack-unwinding)过程中,由异常处理系统删除一个对象。
调用析构函数时,异常可能被激活,也可能未被激活,因为我们无法判断,所以析构函数必须被假设在异常被激活的状态下编写。
如果一个异常被激活的同时,析构函数也抛出异常,并导致程序的控制权转移到析构函数外部,C++将调用terminate函数。它终止程序的运行,立即终止,甚至连局部对象都没有释放。!!!
例如对某类的析构函数:
SomeClass::~SomeClass()
{
std::cout << “destructor is called !”;
}
如果在异常处于激活状态时,该析构函数抛出一个异常,因为异常未在析构函数内部进行捕获,所以被传到外部,terminate被调用,不论你希望与否,程序被粗暴的终止了!即使事态并未如此严重。
解决这种问题的一种方法就是将可能抛出异常的部分放在try{}中,用catch(…){}捕获,这可以防止异常传到外部。
第二个不允许异常传到析构函数外部的原因是:如果一个异常被析构函数抛出而未被在内部进行捕获,那么析构函数无法完全运行(异常处理机制就是这么规定的,不论你喜欢与否),而如果析构函数无法完全执行,很可能无法完成你希望完成的所有事情,你应该避免这些事情的发生。