class A
{
public:
A()
{
cout << "construction fun" << endl;
throw 1;
}
~A()
{
cout << "destruction fun " << endl;
throw 2;
}
} ;
int main()
{
try
{
A a;
}
catch(...)//catch all
{
cout << "caught!" << endl;
}
system("pause");
return(0);
}
// 输出
// construction fun
// caught!
// 注释掉"throw 1",输出
// construction fun
// destruction fun
// caught!
“构造函数中如果抛出异常,可见析构函数并没有被调用,这样子的话就产生了内存泄漏,所以如果要在构造函数抛出异常之前,应该先把已经成功分配的资源释放掉.
在C++中,经典的解决方案是使用STL的标准类auto_ptr,并且不要做过多的事情,只是能对成员变量的做初始化工作就好了。真的需要做其他复杂的初始化操作,完全可以提供一个Init或Start函数.
析构函数:
C++ 并 没有禁止析构函数引发异常,但 是 C++ 十分不推荐这一做法。”
=================================================================================
=================================================================================
对于出错处理,在C语言时代,使用的方法就是返回一个错误代码。预定义一系列的代码标识,当发生指定的错误时候,调用过程返回对应该类型错误的代码。
这种方法简单,但是不适合复杂的应用。它会导致若干的问题,比如:
1.质量下降。使用错误代码,那么必然需要在处理中对不同的代码进行分支处理。而分支过程包含错误可能性是其他方式的10倍。消除分支,代码将更加健壮。
2.增加成本。一方面,由于分支,那么白盒法测试时的测试条件个数增多,延长测试过程;另一方面,控制流程的复杂性导致维护成本上升。
3. ....
然而更糟糕的是,C++的构造/析构函数不允许有返回值,没有返回类型,所以使用错误代码的方法被一锤打死了。也许有人会想到使用出参的方式返回错误代 码,但是这个方法对于OO特性是存在冲突的,其详细分析可参见参考资料2。因此,通知对象的构造失败的唯一方法那就是在构造函数中抛出异常。
构造函数异常,可以总结如下:
1.C++中通知对象构造失败的唯一方法那就是在构造函数中抛出异常;
2.构造函数抛出异常时,析构函数将不会被执行;
3.抛出异常时,其子对象将被逆序析构。(参考析构过程)
析构函数异常相对要复杂一些,存在一种冲突状态,程序将直接崩溃:异常的被称为“栈展开(stack unwinding)”【备注】的过程中时,从析构函数抛出异常,C++运行时系统会处于无法决断的境遇,因此C++语言担保,当处于这一点时,会调用 terminate()来杀死进程。因此,当处理另一个异常的过程中时,不要从析构函数抛出异常。概括总结如下:
1.C++中析构函数的执行不应该抛出异常;
2.当在某一个析构函数中会有一些可能(哪怕是一点点可能)发生异常时,那么就必须要把这种可能发生的异常完全封装在析构函数内部,决不能让它抛出函数之外(这招简直是绝杀!呵呵!);
3.抛出异常时,其子对象将被逆序析构。(参考析构过程)
参考资料:
[1] 异常和错误处理(C++ FAQ),http://www.sunistudio.com/cppfaq/exceptions.html
[2] 构造函数中抛出的异常, http://51cmm.csai.cn/ExpertEyes/No143.htm
[3] 析构函数中抛出的异常, http://se.csai.cn/ExpertEyes/No144.htm
备注:
栈展开(stack unwinding):举例来说,如果某人写了throw Foo(),栈会被展开,以至throw Foo()和 } catch (Foo e) { 之间的所有的栈页面被弹出。这被称为栈展开(statck unwinding)