C++中异常处理对于一个错误的默认响应方式是终止程序。
C++中的异常处理机制是一种非局部的控制结构,基于堆栈回退,因此也可以看做是另一种返回机制,存在着许多并不是处理错误的对异常的合法使用。
一个异常也就是某个用于表示异常发生的类的一个对象,检查到一个错误的代码段throw一个对象,一个代码段用catch子句表明它要处理某个异常,一个throw的作用就是导致堆栈的一系列回退,直到找到某个适当的catch。在堆栈回退的过程中,将会对所有构造起来的局部对象调用析构函数,此时的析构函数如果抛出异常,将会导致terminate,所以尽量不要在析构函数中抛出异常。
原则上讲,异常在抛出时将被复制,所以,异常处理器得到的只是原始异常的一个副本,事实上,一个异常可能在被捕捉之前复制过许多次,因此,不应该抛出一个不允许复制的异常。系统也可以保证,系统总存在足够的存储,使new可以抛出一个bad_alloc异常。
异常处理的异常捕捉将根据catch的顺序检查,而非类似函数的类型匹配检查。
尽量使用“资源申请即初始化”技术来进行资源申请类操作。
构造函数本身可以通过将完整的函数体(包括成员初始式表)包在一个try块里,用来捕捉初始式产生的异常,如:
X::X(int s)
try
:v(s){}
catch(...){}
异常的描述:void f(int a) throw (x2, x3);//申明描述了将抛出的异常保证,如果这个函数的执行中作了某种事情,试图废止所做出的保证,这个企图将会被转换为一个对std::unexpected()的调用,unexpected()的默认意义是std::terminate(),它转而调用abort()。unexpected()不会返回。
相应的:
void f(int a);//可能跑出任何异常
int g() throw();//不会抛出异常
异常的范围相关,子类的覆盖函数的异常描述集合要<=父类函数的异常描述。对函数指针赋值时,可以将一个异常描述范围小的赋给一个异常描述范围大的函数指针,反过来则不能,所以不能将一个没有异常描述(可以抛出任何异常)的函数指针赋值给带异常描述的函数指针。
异常描述不是函数类型的一部分,typedef不能带有异常描述。
对于未预期的异常,即抛出了异常描述范围外的异常,会直接触发unexpected()终止程序,可以采取几种方法转换抛出的未预期异常。
一是将标准库异常std::bad_exception加入到异常描述,这样unexpected()会直接抛出bad_exception,这样至少可以防止立即terminate()。
二是更改unexpected的默认处理函数(默认应该是terminate()),方式与new失败的处理方式类似:
typedef void (*unexpected_handler)();
unexpected_handler set_unexpected(unexpected_handler);//这样,发生unexpected会转而调用unexpected_handle指针指定的函数。
如:
class STC{
unexpected_handler old;
public:
STC(unexpected_handler f){old = set_unexpected(f);}
~STC(){set_unexpected(old);}
};//采用资源申请即初始化 方式。
void throwY() throw(Yerr) {throw Yerr;}//Yerr是个异常对象
void g() throw(Yerr){
STC xx(&throwY);//改变unexpected的意义
...;//如果抛出了Yerr之外的异常会自动调用throwY转而抛出Yerr。
}
如果抛出的异常未被捕捉,就会调用std::terminate(),当异常处理发现堆栈损坏,或者在由某个异常抛出而导致的堆栈回退过程中,被调用的析构函数再抛出异常,都会导致调用terminate()。对未捕捉的异常的响应由_uncaught_handler确定,由std::set_terminate()设置。(未预期的异常由set_unexpected设定,new的异常处理由set_new_handler设定)
一般的,我们可以在main中设定捕捉所有异常,但对全局变量初始化期间抛出的异常没有办法能够捕获。这也是应该尽可能的避免全局变量的原因之一。
标准异常列表:
bad_alloc: 由new操作抛出,位于<new>;
bad_case: 由dynamic_cast抛出,位于<typeinfo>;
bad_typeid: 由typeid抛出,位于<typeinfo>;
bad_exception: 当抛出未预期的异常时抛出,位于<exception>;
out_of_range: 由at(),bitset<>::operator[]()抛出,位于<stdexcept>;
invalid_argument: 按位设置构造函数时,位于<stdexcept>;
overflow_error: 在bitset<>::to_ulong()抛出,位于<stdexcept>;
ios_base::failure: ios_base::clear(),位于<ios>;
所有的异常的根都是exception.