【C++ Exceptions】exception specifications的利与弊、非预期异常的预防与处理

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的方法

  1. 避免让模板和exception specifications放在“需要类型自变量”的模板身上,最好是不要让模板和异常规格混用;
  2. 如果A函数内调用了B函数,而B函数无exception specifications,那么A函数本身也不要设定exception specifications(极易忽略的一种情况:允许用户注册回调函数的时候);
  3. 处理“系统”可能抛出的exception (最常见的就是bad_alloc,当内存分配失败的时候它被operator new和operator new[ ]抛出)。

直接处理unexpected exceptions的方法

  1. C++允许以不同类型的异常替换非预期的异常;
  2. 把非预期的异常转换成已知类型方法来替换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加入函数之前,需要再三审度它的行为效果及后果。

你可能感兴趣的:(C++进阶,c++,笔记,开发语言)