1.异常描述
抛出或捕捉异常对某个函数与其他函数的关系产生影响。抛出异常集合作为函数声明的部分就具有非常重要的价值(前面已经简单提过空抛出的问题)。例如,
其中,函数function(inta)可能抛出两个异常类x2和x3的对象以及从这两个异常类型派生的异常,不会抛出其他类型异常。若函数描述了可能抛出的异常,即有效地为其调用者提供了一种保证。在执行过程中,函数试图废止所做的保证,此意图会被转换成一个对std::unexpected()的调用。unexpected()的默认意义是std::terminate(),一般会转而调用a-bort()函数。
在功能方面:
上述代码等价于:
上述代码的优点在于函数的声明属于界面(即接口),而界面是函数的调用者明显可以看到的。函数的定义通常不是可用的内容。即使有机会访问所有库的源代码,程序员也不希望经常去查看库函数的源代码。另外,带有异常描述的函数要比使用try块和catch块简捷得多。
如果函数声明中不包含异常描述,假定该函数可能抛出任何其他异常,但又不希望抛出某些异常,可以使用空抛出声明形式(前面已提到)。例如,
2.异常描述检查
编译过程可能会遗漏违反界面描述的情况。编译时的检查可以完成大部分工作。异常描述的方式应该是假定函数将要抛出或可能要抛出的所有异常。如果函数的声明中包含了异常描述,该函数的每个声明(或定义)都必须包含完全一致的异常类型集合的异常描述。
若在函数声明时包含了throw(),而在函数的定义时没有包含throw(),则是错误的。例如,
以上代码就是错误的。
上述对异常描述进行了较详细的讲解,读者也没必要为此恐慌。当某处发生异常时,程序员不必强制性地相关的异常描述做完全的更新,不必强制性地要求重新编译受影响的代码。大多数系统应该能够在一种部分更新的状态中依靠对未预期异常的动态检查照常工作。对于大规模系统的维护是必不可少的,但大规模更新的代价极其昂贵。
尤其,当需要覆盖一个虚函数时,函数所带的异常描述必须至少是和那个虚函数的异常描述一样受限的。
总而言之,异常处理是非常复杂的过程。读者只要掌握最简捷的使用形式即可,确保在使用过程中万无一失即可,没有必要去追求使用形式的多样性和复杂性。
3.未预期的异常
异常描述可能导致调用unexpected()函数。如果不希望出现此种调用的情况,可在程序调试期间,通过细心地组织异常和界面描述,尽力避免此种调用,即尽力拦截对unexpected()的调用,并使之无害化。(www.xing528.com)
unexpected()函数的声明形式如下:
C++标准要求表明:如果被抛出的异常不包括在throw列表中,unexpected()会被调用。当前的实现不支持这种情况,例如直接调用unexpected()时,是通过调用该函数的句柄。如果unexpected()函数在程序中被直接调用时,unexpected()的句柄可以通过set_unexpected()设置。其使用方法可参考例8-4。unexpected()处理器不可能返回至其调用者,它可能通过以下条件终止执行:
1)异常描述中的类型列表中类型的对象,或任意类型的对象被抛出时,unexpected()处理器可以在程序中直接调用。
2)抛出bad_exception类型的对象。
3)在调用terminate()、abort()或者exit()过程中。
set_函数unexpected()的声明形式如下:
参数Pnew是指定的被调用的函数,函数返回值是原有的被调用的函数。
通常,设计良好的子系统Y的所有异常均从类Yerr中派生。例如,
包含如下声明的函数:
当把所有Yerr传递给它的调用者。特别是function()可以通过将Some_Yerr传给其调用者的方式去处理它。function()函数中的Yerr不会触发unexpected()。由标准库中抛出的所有异常均是由exception派生出的。
4.异常映射
若某个异常一旦发生即终止程序,此种策略无疑是过于严厉的。此时,需要将unex-pected()的行为修改为其他易于接受的方式。
最简便的方法是将标准库异常std::bad_exception加入某个异常描述中。此时,unex-pected()会抛出一个bad_exception,而不去调用某个难以处理的函数。例如,
异常描述将捕捉未预期的异常Y,之后再抛出一个bad_exception类型的异常。
类bad_exception仅仅是提供一种机制,并没有调用terminate()那样“冷酷”,但丢失了引起问题的那个异常的所有信息。
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。