1. 传统的C中处理错误的方法有三种:(1)使用全局状态本标记来标识异常状态,或是函数返回值区别错误类型;(2)使用鲜为人知的C中的signal和raise库函数提供的信号处理系统;(3)使用C库中的非局部跳转函数:setjmp()和longjmp(),这种非局部跳转函数与goto的区别是:goto只在一个函数中进行跳转。这些方法的缺点在于,信息不丰富,各种错误信号要定义,对应关系及其含义很烦琐,不系统,而且方法(3)还不能调用析构函数,故在C++中不适用,因而引用了异常机制。
2. C++中的异常可以是任何类型(Java中只能是Exception的子类);但最好设计特定的异常类。异常抛出后,等于将函数返回值类型设成了所抛对象的类型,但与return不同的是,异常抛出后返回的地方是异常处理器(catch模块),而非函数调用的后续语句,此外,异常发生之前定义的局部对象将被销毁,这种行为称为栈反解(stack unwinding)。
3. catch匹配时,支持向上转型,但不支持自动类型转换,最好不用值捕获而用引用,避免切片。
4. catch(...)捕获所有异常,重新抛出异常用throw;重新抛出异常会将异常传递给更高一级的异常处理器。
5. 所有层次的异常处理器都未作出处理的异常,会到最外层调用terminate函数。它默认调用C库函数abort,使程序执行异常中止而退出,执行abort时,全局对象和静态对象的析构函数不会被调用。
6. 通过标准的set-terminate函数可以设置自己的terminate函数,该方法返回被替换的terminate函数的指针,以备恢复时用。
7. 在下面两种情况下terminate函数也会被执行:(1)局部对象的析构函数抛出异常时,正在进行栈返解(处理异常时再抛新异常);(2)全局或静态对象的构造或析构函数抛出一个异常(因为无法定义catch块),故一般不是构造和析构函数中抛出异常。
8. 为防止构造函数中抛出异常,导致析构函数未被调用,而已分配的空间无法释放,方法有二:在方法内捕获并处理异常,释放空间;(2)以及使用智能指针释放资源。
9. 异常规格说明:
void f();//可抛出任何异常
void f() throw();//不抛出任何异常
void f() throw(tosmall,tobig,divzero)//可抛出三种异常
10. 如果异常规格说明中未包含所抛出的异常,则会引发一个unexpected函数,它会调用前面提到的terminate方法。同样可以用set-unexpected设置自定义的unexpected方法。
11. unexpected方法可以抛出一个异常,如果该异常匹配,则catch块工作,否则下面情形之一将会发生:
(1)如果异常规格说明中包括std::bad_exception则unexpected会抛出一个bad_exception,然后重新匹配各块catch
(2)否则执行terminate
12. 异常规格说明主要用于非模板类,模板无法确定有哪些异常类。