《第十二章异常处理b剖析.》由会员分享,可在线阅读,更多相关《第十二章异常处理b剖析.(48页珍藏版)》请在金锄头文库上搜索。
1、第十二章 异常处理,C+语言程序设计,本章主要内容,异常处理的基本思想 C+异常处理的实现机制 异常处理中的构造与析构 标准程序库异常处理,异常处理的基本思想,在一个大型软件中,由于函数之间有着明确的分工和复杂的调用关系,发现错误的函数往往不具备处理错误的能力。 C+语言异常处理机制的基本思想是将异常的检测与处理分离。当在一个函数体中检测到异常条件存在,但却无法确定相应的处理方法时,该函数将引发一个异常,由函数的直接或间接调用者捕获这个异常并处理这个错误。,异常处理的意义,一个好的程序不仅要保证能实现所需要的功能,而且还应该有很好的容错能力。在程序运行过程中如果有异常情况出现,程序本身应该能解
2、决这些异常,而不是死机。本章介绍异常处理的基本概念、C+异常处理语句、析构函数与异常处理。通过本章的学习,掌握了C+异常处理的机制,我们就可以在编制程序时灵活地加以运用,从而使我们编制的程序在遇到异常情况时能摆脱大的影响,避免出现死机等现象。,异常(Exception):指程序在执行过程中出现的意外情况,异常通常会使程序的正常流程被打断,常见的错误有: 用户输入错误 设备故障 物理限制 代码错误 异常处理:是一种程序定义的错误,它对程序的逻辑错误进行设防,对运行异常加以控制 C+中,异常处理是对所能预料的运行错误进行处理的一套实现机制 异常处理的任务就是使程序的运行过程能从异常错误中恢复过来继
3、续执行,传统的异常处理方法:传统的C语言主要有三类设计策略 返回一个状态码来表明成功或失败 把错误码赋值给一个全局标记并让其他的函数来检测 二者的相似之处:都提供一种机制来报告错误,但是二者却都 不能保证错误被处理 通过调用系统函数( or )终止整个程序 exit() 表明程序被成功终止,或者它可以在遇到运行期错误的时候被调用 在把控制权交还给运行环境之前用户程序首先会自动清空流和关闭打开的文件 abort():表示程序被意外终止,不会清空流和关闭打开的文件,传统的异常处理方法存在的问题 异常的检测和异常的处理不能分离处理。如果某个类模块或函数模块仅提供通用的处理方法,而当出现异常时,由调用
4、函数根据要完成的任务,自行考虑异常处理方法,这样的处理模式就无法方便地实现。 调用一个函数时出现的异常信息要返回给调用函数,供调用函数根据返回的异常信息的情况酌情处理,甚至这种异常信息的传递会有一级以上,这时传递的异常处理方法实现起来就很困难。 exit()和abort()不销毁对象,并不调用局部对象的析构函数,可能造成内存泄露问题。,对于一个实用的程序而言,没有错误处理是不可能的。实际的(大型)程序是由许多人在不同的时间内开发出来的,许多出错处理任务并不一定能在(或者不应当在)发现出错的地方完成。 一个欠缺良好结构的程序(有时也可能是由于没有语言支持机制造成的),对其错误通常采用判断语句(如
5、C+中的if和switch语句)进行判断与处理。这种错误的判断与处理方式带来两个问题:,大量的错误处理代码与程序的功能代码交织在一起,这不仅造成了程序结构的混乱不堪,而且往往错误处理代码远远大于程序的功能代码,这使得程序的可读性与可维护性大大下降。 对于某些错误而言,程序不知如何或不能够处理,因而对此类错误只能丢弃,这又往往使程序的可靠性大大下降,甚至在某些极端的情况下,程序退化成不可用。,欲正确处理一个错误,需要明确知道如下两类信息: (1) 错误发生的地点,何种类型的错误; (2) 怎样处理错误,在何处处理错误。,出错处理任务应当被分解成两部分:错误处理与错误的报告。 (1) 在某处(更一
6、般地说是在某一模块)若发现错误(该错误可能是本模块中的错误,亦可能是来自其它模块中的错误),能处理,则进行处理。 (2) 不能处理则应设置出错报告的条件,当满足条件时进行报告,以提供必要的信息,供可能进行错误处理的模块进行错误处理。,C+的异常处理机制(Exception Handling)提供了一种结构化的、能有效地捕获和处理各种类型的错误的机制。 该机制将错误的报告与错误的处理显式分离,并使得系统能从导致异常的错误中恢复,恢复的过程即执行异常处理程序(Exception Handler)。,如果程序始终没有处理这个异常,最终它会被传到C+运行系统那里,运行系统捕获异常后,通常只是简单地终止
7、这个程序。 由于异常处理机制使得异常的引发和处理不必在同一函数中,这样,底层的函数可以着重解决具体问题而不必过多地考虑对异常的处理;上层调用者可以在适当的位置设计对不同类型异常的处理。,异常处理的基本思想,链式调用与出错后跳跃返回,主函数,文件名 处理,打开方 式处理,打开输 入文件,打开输 出文件,出错跳回,错误处理示意:,放弃一棵子树,循调用链跳到祖先函数,发现错误处,异常超脱于函数机制,决定了其对函数的跨越式回跳。,异常处理的实现机制,传统C的错误处理方法并不适合C+,C+的一个设计目标就是让用C+进行大规模软件开发比C更好更安全。 在当异常被触发的时候程序自动把控制权传递给系统。机制必
8、须简单,并且它能够使程序员从不断的检查一个全局标记或者返回值的苦差事中解脱出来。另外,它还必须保证异常处理程序能够自动获得异常信息。最终它还要确保当一个异常没有在本地处理的时候,本地对象能够被适当的销毁,并且把它所持有的资源释放。,异常处理的基本思想:将异常的抛出和异常的处理进行分离 底层的函数可以着重解决具体的问题,而不必过多地考虑对异常的处理 上层调用者可以在适当的位置设计对不同类型异常的处理,这在大型程序中是非常必要的 异常处理的基本方法 当程序中出现异常时抛出异常,用来通知系统发生了异常,然后由系统捕捉异常,并交给预先安排的异常处理程序段来处理异常 C+异常处理结构,异常使用三部曲,1
9、.框定异常(try 语句块) 在祖先函数处,框定可能产生错误的语句序列,它是异常的根据,若不框定异常,则没有异常。 2.定义异常处理(catch 语句块) 将出现异常后的处理过程放在catch块中,以便当异常被抛出,因类型匹配而捕捉时,就处理之。 3.抛掷异常(throw 语句) 在可能产生异常的语句中进行错误检测,有错就抛掷异常,try /可能引发异常的代码 catch(type_1 e) / type_1类型异常处理 catch(type_2 e) / type_2类型异常处理 catch(type_n e) / type_n类型异常处理 catch() / 任何类型异常处理, /必须是c
10、atch块的最后一段处理程序 ,异常处理的实现机制,抛掷异常的程序段 throw 表达式; ,捕获并处理异常的程序段 try 复合语句 catch(异常类型声明) 复合语句 catch(异常类型声明) 复合语句 ,例12-1处理除零异常,#include int Div(int x,int y); int main() try cout“5/2=“Div(5,2)endl; cout“8/0=“Div(8,0)endl; cout“7/1=“Div(7,1)endl; catch(int) cout“except of deviding zero.n“; cout“that is ok.n“;
11、 int Div(int x,int y) if(y=0) throw y; return x/y; ,程序运行结果如下: 5/2=2 except of deviding zero. that is ok.,#include int Div(int x,int y); int main() cout“5/2=“Div(5,2)endl; cout“8/0=“Div(8,0)endl; cout“7/1=“Div(7,1)endl; cout“that is ok.n“; int Div(int x,int y) return x/y; ,程序运行结果如下:?,异常处理的过程,若有异常则通过t
12、hrow操作创建一个异常对象并抛掷。 将可能抛出异常的程序段嵌在try块之中。控制通过正常的顺序执行到达try语句,然后执行try块内的保护段。 如果在保护段执行期间没有引起异常,那么跟在try块后的catch子句就不执行。程序从try块后跟随的最后一个catch子句后面的语句继续执行下去。 catch子句按其在try块后出现的顺序被检查。匹配的catch子句将捕获并处理异常(或继续抛掷异常)。 如果匹配的处理器未找到,则运行函数terminate将被自动调用,其缺省功能是调用abort终止程序。,异常处理语句 捕捉异常 当程序运行时未出现异常,则顺序执行 try 模块中的语句 一旦系统捕捉到
13、异常发生,就中止当前的程序执行,转去执行相应的catch语句中的异常处理语句 处理异常,try 复合语句 ,catch() /异常处理语句 ,异常处理语句中可包含返回语句,则整个程序结束。 不包含返回语句,则异常处理完成后,顺序执行 catch 语句后的语句。 特别处理 如果匹配的处理程序未找到,则函数terminate()将被自动调用,而函数terminate()的默认功能是调用abort()函数终止程序。 在Visual C+中,如果以abort函数终止程序,则会在 debug 模式运行时弹出如下图所示对话框。,异常抛出和处理的两种方式: 在同一个函数中抛出异常和处理异常。 在一个函数中抛
14、出异常,在调用该函数的函数中处理异常。,异常抛出和处理方式的选取 对一个公共类来说,如果所有应用程序对于某个异常的处理方法都相同时,则该异常的抛出和处理应该采用第一种方式,这样对于使用该公共类的外部程序来说,代码设计简洁,无需使用try语句和catch语句 对一个公共类来说,如果所有应用程序对于某个异常的处理方法不相同时,则该异常的抛出和处理应该采用第二种方式,这样对于使用该公共类的外部程序来说,就可以根据各自问题的要求,考虑不同的异常处理方法,异常接口声明,为了加强程序的可读性,使用户能够方便地知道所使用的函数会抛掷哪些异常,可以在函数的声明中列出这个函数可能抛掷的所有异常类型。,doubl
15、e jie(double, double, double) throw(int, float, double, char); 则表示jie()函数可以抛出int,float,double或char类型的异常信息。,可以在函数的声明中列出这个函数可能抛掷的所有异常类型。 例如: void fun() throw(A,B,C,D); 若无异常接口声明,则此函数可以抛掷任何类型的异常。 void fun(); 不抛掷任何类型异常的函数声明如下: void fun() throw();,异常处理中的构造与析构,C+异常处理的真正能力不仅在于它能够处理各种不同类型的异常; 还在于它具有在异常抛掷前为构造
16、的所有局部对象自动调用析构函数的能力。,如果类对象是在try块(或try块中调用的函数)中定义的,在执行try块 (包括在try块中调用其他函数) 的过程中如果发生了异常,此时流程立即离开try块(如果是在try块调用的函数中发生异常,则流程首先离开该函数,回退到调用该函数的try-catch结构的catch块处)。这样流程就有可能离开该对象的作用域而转到其他函数。 所以应当事先做好结束对象前的清理工作,C+的异常处理机制会在throw抛出异常信息被catch捕获时,对有关的局部对象调用析构函数,然后再执行与异常信息匹配的catch块中的语句。,找到一个匹配的catch异常处理后 初始化参数。 将从对应的try块开始到异常被抛掷处之间构造(且尚未析构)的所有自动对象进行析构。 从最后一个catch处理之后开始恢复执行。,例12-2 异