exception specifications利与弊
- 漂亮的注释;
- 编译器有时能在编译过程中,检测到与exception specifications不一致的行为,这时unexpected会默认调用terminate,terminate默认调用abort,程序被中止;
该情况极易发生:
编译器仅作局部检验,未检测到的是:
- 一个函数调用另外一个函数 ,后者可能违反调用端函数的exception specifications
eg.
extern void f1(); //可以抛出任何一种异常
void f2() throw(int);//只能抛出int类型异常
void f2() throw(int)
{
...
f1(); //即使f1可能抛出非int异常也合法
...
}
- 会造成“当一个较高层次的调用者已经准备好要处理发生的exception时,unexpected函数却被调用”的现象。
class Session
{
public:
~Session();
...
private:
static void logDestruction(Session* objAddr) throw();
};
Session::~Session()
{
try{
logDestruction(this);
}
catch(...)
{
}
}
//假设logDestruction调用的函数抛出异常,而logDestruction没拦住
//当非预期异常传到logDestruction,unexpected函数被调用,程序终止
//Session析构函数的catch块尚未执行程序就已经中止了
//所以将logDestruction的exception specifications去掉
事先预防unexpected exceptions的方法
- 避免让模板和exception specifications放在“需要类型自变量”的模板身上,最好是不要让模板和异常规格混用;
- 如果A函数内调用了B函数,而B函数无exception specifications,那么A函数本身也不要设定exception specifications(极易忽略的一种情况:允许用户注册回调函数的时候);
- 处理“系统”可能抛出的exception (最常见的就是bad_alloc,当内存分配失败的时候它被operator new和operator new[ ]抛出)。
直接处理unexpected exceptions的方法
- C++允许以不同类型的异常替换非预期的异常;
- 把非预期的异常转换成已知类型方法来替换unexpected,重新抛出当前异常,所有异常将被替换成bad_exception,这个异常替代原来的异常继续传递。
1.
class UnexpectedException{}; //所有的非预期异常都被它取代
void covertUnexpected() //如果一个非预期异常抛出,便调用此函数
{
throw UnexpectedException();
}
//并以covertUnexpected取代默认的unexpected函数
set_unexpected(covertUnexpected);
2.
void covertUnexpected() //如果非预期异常被抛出,此函数便调用,它只是重新抛出当前异常
{
throw;
}
set_unexpected(covertUnexpected); //安装covertUnexpected,作为unexpected函数的替代
//任何非预期异常都被bad_exception取代,该异常取代原来的异常继续传递下去
总结
exception specifications是一把双刃剑,清晰说明了函数希望抛出怎样的异常,但是一旦违反就面临程序必须立刻中止的危险。编译器仅对exception specifications做局部的检验,很容易在不经意间违反,而且exception specifications还会妨碍更高层的异常处理函数处理未预期的异常,就算较高层已经知道怎么做了还是可能被提前中止,所以,在将exception specifications加入函数之前,需要再三审度它的行为效果及后果。