微波EDA网,见证研发工程师的成长!
首页 > 硬件设计 > 嵌入式设计 > 异常机制简单探讨

异常机制简单探讨

时间:11-22 来源:互联网 点击:

3 合理地处理异常
由于后面的讨论多处涉及到“栈展开”这个概念,这里先解释一下。“栈展开”是异常机制中一个重要的过程:在逐层查找用来处理异常的catch子句时,因为异常而退出复合语句和函数定义,这个过程被称作“栈展开”。随着栈的展开,在退出的复合语句和函数定义中声明的局部变量的生命期也结束,而且这些局部类对象的析构函数也会被调用,这样能保证内存空间得到合理回收。栈展开的概念对于理解后面的内容很重要,我们通过一个具体例子进一步阐述。


当异常发生时,在函数调用链中逐层查找该异常的catch子句。在栈展开过程中函数foo()首先被检查到,因为产生异常的语句没有被放在try块中,所以不会在:foo()中查找针对该异常的catch子句。栈展开过程继续向上遍历函数调用链到达调用foo()的函数。然而在foo()带着这个未处理的异常退出之前,栈展开过程会销毁foo()中所有在异常产生之前被创建的局部类对象。结果就是:o1、o2的析构函数被调用,o3已经“死亡”,而o4还没“出生”。
顾名思义,“异常”就是程序运行出现了非预期的情况,或者说错误。因此,出现异常必须有针对地处理。对此,MISRA C++首先有如下规定:
规则15-3-4(强制):所有可能的流程中显式抛出来的异常都应该有一个类型兼容的处理程序。
规则15-3-2(推荐):至少要有一个处理程序来处理所有其他未针对处理的异常。
如果程序抛出一个没有被处理的异常,程序会终止,而终止前调用栈有没有被“展开”,动态对象能不能被析构,这些都依赖于编译器。上面两条规则规定了:不但预期抛出的异常要进行处理,其他可能被抛出的异常也要有相应的处理措施。请注意规则15-3-4中“类型兼容”的字眼,C++有非常灵活的类型兼容规则,尤其对于类。例如当异常对象是派生类时,“兼容类型”可以是派生类,也可以是基类。后面我们还会具体讨论这个问题。
一个try块后可以有多个catch块来捕获不同的异常。当出现异常时,catch处理程序按照其在try块后出现的顺序被逐个检查,只要找到一个匹配的异常类型,后面的异常处理都被忽略。因此,catch处理程序出现的顺序很重要。
规则15-3-6(强制):若一个try-catch语句块有多个处理程序,或者一个派生类和其部分或全部基类的function-try-block块有多个处理程序,处理程序的顺序应该是先派生类后基类。
规则15-3-7(强制):若一个try-catch语句块或者function-try-block块有多个处理程序时,catch(…)处理程序(捕获所有异常)应该放在最后。
这是因为根据类型兼容规则,异常对象为派生类时可以被针对基类的处理程序所捕获。如果针对基类的处理程序放在前面,后面针对派生类的处理程序就不会被执行到。同理,catch(…)处理程序能捕获所有类型的异常,在其后面所有的异常处理程序都不会被执行到。根据上述规则,典型的try-catch的结构示例如下:


细心的读者也许会发现,上面例子中是通过引用来捕获类的对象。当异常对象类型为某个类时,有3种方式传递到catch子句里:指针、传值和引用。也许大家首先想到的是指针,指针的确是效率很高的工具,而且不涉及到对象拷贝。但不要忘了,前面的规则15-0-2中明确指出,抛出的异常对象不应该是指针类型。而对于传值和引用,在MISRA C++中给出的规定是:通过引用捕获异常。
规则15-3-5(强制):若异常对象为类的对象时,应该通过引用来捕获。
通过值传递,不但会增加拷贝对象的开销,而且还会出现“退化”问题。所谓“退化”是指:如果异常对象是一个派生类对象,但被作为基类捕获,那么只有基类的函数(包括虚函数)能被调用,派生类中增加的数据成员都不能被访问。通过引用捕获则没有这个问题。下面的例子具体地说明了“退化”问题:


鉴于类的构造函数和析构函数的特殊性,还有两点需要注意。
规则15-3-3(强制):如果类的构造函数和析构函数是function-try-block结构的,在catch处理程序中不能引用该类或其基类的非静态成员。
这种行为的后果是不定的。比如说,当构造对象分配内存时抛出了异常,这时该对象本身还不存在,访问其成员也就出错。相反,在析构函数里,可能在异常处理程序执行前该对象已被成功销毁了,也就无从访问其成员了。而类的静态成员则没有上述问题。
规则15-5-1(强制)。类的析构函数退出后不能还有未处理的异常。
当异常抛出时,会进行栈展开。如果在某个析构过程中引发没有被处理的异常,程序将会以不定的方式终止。析构函数抛出异常的问题在很多C++的书中都有讨论,概括来说:析构函数应尽可能地避免抛出异常,如果的确无法避免,则析构函数自己应该包含处理所有可能抛出的异常的代码。

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top