第十章 异常处理.ppt_第1页
第十章 异常处理.ppt_第2页
第十章 异常处理.ppt_第3页
第十章 异常处理.ppt_第4页
第十章 异常处理.ppt_第5页
已阅读5页,还剩30页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、第十章 异常处理,编写大型和十分复杂的程序,往往会产生一些很难查找的错误。面向对象的异常处理(exception handling)机制是C+语言用以解决这个问题的有力工具。函数执行时,放在try(测试)程序块中的任何类型的数据对象发生异常,都可被throw块抛出,随即沿调用键退回,直到被catch块捕获,并在此执行异常处理,报告出现的异常等情况。从抛出到捕获,应将各嵌套调用函数残存在栈中的自动对象、自动变量和现场保护内容等进行清除。如果已退到入口函数还未捕获则由abort()来终结入口函数。,第十章 异常处理,10.1 异常的概念,10.3 捕获异常,10.2 异常处理的机制,10.5 异常

2、规范,10.7 C+标准库的异常类层次结构,10.6 异常和继承,10.4 异常的重新抛出和catch_all子句,10.1 异常的概念,大的软件不可能没有错误,也不可能在排除所有的错误后才投入使用。找出所有潜在的运行时错误那几乎是不可能的,能够做到的是预计可能发生什么类型的错误,并在错误发生时停止发生错误的操作,对它进行处理,而程序的其它部分仍然继续运行。 这里所讲的异常(exception)是程序可能检测到的,运行时不正常的情况,如存储空间耗尽、数组越界、被0除等等,可以预见可能发生在什么地方,但是无法确知怎样发生和何时发生。特别在一个大型的程序(软件)中,程序各部分是由不同的小组编写的,

3、它们由公共接口连起来,错误可能就发生在相互的配合上,也可能发生在事先根本想不到的个别的条件组合上。然后由预先安排的程序段来捕获(catch)异常,并对它进行处理。这种机制可以在C+程序的两个无关(往往是独立开发)的部分进行“异常”通信。,10.2 异常处理的机制,首先,在C+中异常往往用类(class)来实现,异常类的声明如下: class popOnEmpty.; class pushOnFull.; template void Stack:Push(const T 注意pushOnFull是类,C+要求抛出的必须是对象,所以必须有“()”,这样是调用构造函数建立一个对象。异常并非总是类对象

4、,throw表达式也可以抛出任何类型的对象,如枚举、整数等等。throw表达式为异常处理的第一步。,10.2 异常处理的机制,在C+中建立异常抛出与异常处理之间有一整套程序设计的机制。 请看下面的程序段给出try块与catch字句的关系。 int main()int a9=1,2,3,4,5,6,7,8,9,b9=0,i; stackistack(8); tryfor(i=0;i9;i+)istack.Push(ai); istack.PrintStack(); catch(pushOnFull) cerr”栈满”endl; tryfor(i=0;i9;i+) bi=istack.Pop();

5、 catch(popOnEmpty) cerr”栈空”endl; for(i=0;i9;i+)coutbit; coutendl; return 0; ,10.2 异常处理的机制,程序按下列规则控制: 1如果没有异常发生,继续执行try块中的代码,与try块相关联 的catch子句被忽略,程序正常执行,main()返回0。 2当第一个try块在for循环中抛出异常,则该for循环退出,try块也退出,去执行pushOnFull异常的catch子句。istack.PrintStack()不再执行,被忽略。 3如果第二个try块调用Pop()抛出异常,则退出for和try块,去执行popOnEmp

6、ty异常的catch子句。 4当某条语句抛出异常时,跟在该语句后面的语句将被跳过。程序执行权交给处理异常的catch子句,如果没有catch子句能够处理异常,则交给C+标准库中定义的terminate()。,10.2 异常处理的机制,在编制程序中有一条惯例:把正常执行的程序与异常处理两部分分隔开来,这样使代码更易于跟随和维护。 把程序的正常处理代码和异常处理代码分离的最清楚的方法是定义函数try块(function try block)。这种方法是把整个函数包括在try块中: int main() try int a9=1,2,3,4,5,6,7,8,9,b9=0,; stack istack

7、(8); .; return 0; catch(popOnEmpty)cerr”栈空”endl;return 1; catch(pushOnFull)cerr”栈满”endl;return 2;,10.2 异常处理的机制,最后强调:catch子句必须在try块之后;而try块后必须紧跟一个或多个catch子句,目的是对发生的异常进行处理。catch的括号中只能有一个类型,当类型与抛掷异常的类型匹配时,称该catch子句捕获了一个异常,并转到该块中进行异常处理。,10.3 捕获异常,catch子句由三部分组成:关键字catch、圆括号中的异常声明(exception declaration)以及

8、复合语句中的一组语句。 catch子句可以包含返回语句(return),也可不包含返回语句。包含返回语句,则整个程序结束。而不包含返回语句,则执行catch列表之后的下一条语句。 异常声明中也可以是一个对象声明。还是以栈为例。pushOnFull类可如下定义: Template class pushOnFull T _value; public: pushOnFull(T i):_value(i) /或等效写为pushOnFull(int i)value=i; T value()return _value; ;,10.3 捕获异常,新的私有数据成员_value保存那些不能被压入栈中的值。该值即

9、调用构造函数时的实参。对应在throw表达式中,构造抛出对象也要有实参: throw pushOnFull(data);/data即Push(const 在catch子句的异常声明中声明了对象eObj,用它来调用pushOnFull类的对象成员函数value()。异常对象是在抛出点被创建,与catch子句是否显式要求创建一个异常对象无关,该对象总是存在,在catch子句中只是为了调用异常处理对象的成员函数才声明为对象,不用类。,10.3 捕获异常,catch子句的异常声明,函数参数声明类似,可以是按值传送,也可以是按引用传递。如果catch子句的异常声明改为引用声明,则catch子句可以直接引

10、用被throw表达式创建的异常对象,而不必创建自己的局部拷贝。对于类类型的异常,其异常声明最好被声明为引用。 catch(pushOnFull ,10.3 捕获异常,寻找匹配的catch子句有固定的过程:如果throw表达式位于try块中,则检查与try块相关联的catch子句列表,看是否有一个子句能够处理该异常,如果有匹配的,则该异常被处理,如果找不到匹配的catch子句,则在主调函数中继续查找。如果一个函数调用在退出时带有一个被抛出的异常,而且这个调用位于一个try块中,则检查与该try块相关联的catch子句列表,看是否有一个子句匹配,如果有,则处理该异常,如果没有,则查找过程在该函数的

11、主调函数中进行。这个查找过程逆着嵌套的函数调用链向上继续,直到找到处理该异常的catch子句。只要遇到第一个匹配的catch子句,就会进入该catch子句,进行处理,查找过程结束。,10.3 捕获异常,在栈异常处理的例子中,对popOnEmpty,首先应在istack的成员函数Pop()中找,因为Pop()中的throw表达式没有在try块中,所以Pop()带着一个异常退出。下一步是检查调用Pop()的函数,这里是main(),在main()中对Pop()的调用位于一个try块中,则可用与该try块关联的catch子句列表中的某一个来处理,找到第一个popOnEmpty类型异常声明的子句,并进

12、入该子句进行异常处理。 在这一过程中,因发生异常而逐步退出复合语句和函数定义,被称为栈展开(stack unwinding)。随着栈展开,在退出的复合语句和函数定义中声明的局部变量的生命期也结束了。,10.3 捕获异常,在退出的域中有某个局部量是类对象,栈展开过程将自动调用该对象的析构函数,完成资源的释放。所以C+异常处理过程本质上反映的是“资源获取是由构造函数实现,而资源释放是由析构函数完成”这样一种程序设计技术。采用面向对象的程序设计,取得资源的动作封装在类的构造函数中,释放资源的动作封装在类的析构函数中,当一个函数带着未处理的异常退出时,函数中这种类对象被自动销毁,资源(包括动态空间分配

13、的资源和打开的文件)释放。栈展开过程决不会跳过封装在类的析构函数中的资源释放动作。,10.3 捕获异常,异常对象是在throw表达式中建立并抛出。步骤:throw表达式通过调用异常类的构造函数创建一个临时对象,然后把这个临时对象拷贝到一个被称为异常对象(exception object)的存贮区中,它保证会持续到异常被处理完。异常不能够保持在未被处理的状态。异常表示一个程序不能够继续正常执行,这是非常严重的问题,如果没有找到处理代码,程序就调用C+标准库中定义的函数terminate()。terminate()的缺省行为是调用abort(),指示从程序中非正常退出。,10.3 捕获异常,对比函

14、数调用和异常处理之间的异同。throw表达式的行为有点像函数的调用,而catch子句有点像函数定义。函数调用和异常处理的主要区别是:建立函数调用所需要的全部信息在编译时已经获得,而异常处理机制要求运行时的支持。与运行时的多态虚函数也是不一样的。,10.4 异常的重新抛出和catch_all子句,当catch语句捕获一个异常后,可能不能完全处理异常,完成某些操作后,该异常必须由函数链中更上级的函数来处理,这时catch子句可以重新抛出(rethrow)该异常,把异常传递给函数调用链中更上级的另一个catch子句,由它进行进一步处理。rethrow表达式为: throw; 但是重新抛出异常的cat

15、ch子句应该把自己做过的工作告诉下一个处理异常的catch子句,往往要对异常对象做一定修改,以表达某些信息,因此catch子句中的异常声明必须被声明为引用,这样修改才能真正做在异常对象自身中,而不是拷贝中。,10.4 异常的重新抛出和catch_all子句,通常异常发生后按栈展开(stack unwinding)退出,动态分配的非类对象资源是不会自动释放的,应该在对应的catch子句中释放。因为我们不知道可能被抛出的全部异常,所以不是为每种可能的异常写一个catch子句来释放资源,而是使用通用形式的catch子句catch_all,格式为: catch(.)代码*/,10.4 异常的重新抛出和

16、catch_all子句,见下例: void fun1() int *res; new res100;/定义一个资源对象 try/代码包括使用资源res和某些可能引起异常抛出的操作 catch(.) delete res;/释放资源对象res; throw;/重新抛出异常 delete res;/正常退出前释放资源对象res; catch_all子句可以单独使用,也可以与其它catch子句联合使用。如果联合使用,它必须放在相关catch子句表的最后。,10.5 异常规范,异常规范(exception specification)提供了一种方案,可以随着函数声明列出该函数可能抛出的异常,并保证该函

17、数不会抛出任何其他类型的异常,在stack类定义中可有: void Push(const T 一个函数的异常规范的违例只能在运行时才能被检测出来。如果在运行时,函数抛出了一个没有被列在它的异常规范中的异常时,则系统调用C+标准库中定义的函数unexpected()。unexpected()缺省操作是调用teminate()。 异常规范只是在函数所抛出的异常,没有在该函数内部处理,而是沿调用链回溯寻找匹配的catch子句时才起作用,,10.5 异常规范,【例10.1】现在可以给出包含栈满异常的较完整的程序段。 异常处理主要是服务于大型程序的,抛出异常和处理异常可以是由不同开发小组完成。,10.6

18、 异常和继承,当类的层次结构用于异常时,异常处理的方式变得更加多样化。 在C+程序中,表示异常的类通常被组成为一个组(group)(即如在前面各节讨论的那样)或者一个层次结构。对由栈类成员函数抛出的异常: class popOnEmpty.; class pushOnFull.; 可以定义一个称为Excp的基类,再从该基类派生出这两个异常类。 class Excp.; class popOnEmpty:public Excp.; class pushOnFull:public Excp.;,10.6 异常和继承,由基类Excp来打印错误信息: class Excp public: void p

19、rint(string msg)cerrmsgendl; ; 这样的基类也可以作为其他异常类的基类: class Excp.;/所有异常类的基类 class stack Excp:public Excp.;/栈异常类的基类 class popOnEmpty:public stackExcp.;/栈空退栈异常 class pushOnFull:public stackExcp.;/栈满压栈异常 class mathExcp:public Excp.;/数学库异常的基类 class zeroOp:public mathExcp.;/数学库零操作异常 class divideByZero:publi

20、c mathExcp.;/数学库被零除异常,10.6 异常和继承,这里被创建的异常类对象是stackExcp类类型,尽管pse指向一个实际类型为pushOnFull的对象,但那是一个临时对象,拷贝到异常对象的存贮区中时创建的却是stackExcp类的异常对象。所以该异常不能被pushOnFull类型的catch子句处理。 在处理类类型异常时,catch子句的排列顺序是非常重要的。当异常被组织成类层次结构时,类类型的异常可以被该类类型的公有基类的catch子句捕获到。如pushOnFull类类型的异常可以由stackExcp或Excp类类型异常所对应的catch子句处理。为了保证pushOnFu

21、ll异常的处理由最合适的catch子句来处理,应有如下顺序: catch(pushOnFull)./处理pushOnFull异常 catch(stackExcp)./处理栈的其他异常 catch(Excp)./处理一般异常 派生类类型的catch子句必须先出现,以确保只有在没有其他catch子句适用时,才会进入基类类型的catch子句。,10.6 异常和继承,类层次结构的异常同样可以重新抛出(rethrow),把一个异常传递给函数调用列表中,更上层的另一个catch子句。形式仍为 throw; 虚函数是类层次结构中多态性的基本手段,异常类层次结构中也可以定义虚拟函数。,10.6 异常和继承,【

22、例10.2】异常层次结构中的虚函数。 class Excp public: virtual void print()cerr”发生异常”endl; ; class stackExp:public Excp public: virtual void print()cerr”栈发生异常”endl; ; class pushOnFull:public stackExcp public: virtual void print()cerr”栈满,不能压栈”endl; ;,10.6 异常和继承,int main() try/抛出一个pushOnFulll异常 catch(Excp/调用虚函数pushOnF

23、ull:print() ,10.6 异常和继承,对异常规范(exception specification)首先,异常规范可以在类成员函数后面指定,与非成员函数一样,成员函数声明的异常规范也是跟在函数参数表的后面。如果成员函数被声明为const或volatile成员函数,则异常规范跟在函数声明的const和volatile限定修饰符之后。 第二,如果成员函数在类体外定义,则定义中所指定的异常规范,必须与类定义中该成员函数声明中的类异常规范相同,也就是必须在两处都有相同的异常规范,注意这和函数参数缺省值只能在一处说明(通常在声明中)不同。,10.6 异常和继承,第三,虚函数不同。基类中的虚函数的

24、异常规范,可以与派生类改写的虚函数的异常规范不同。但这不同指的是派生类的虚拟函数的异常规范必须与基类虚函数的异常一样或更严格(是基类虚函数的异常的子集)。如: class CBase public: virtual int fun1(int) throw(); virtual int fun2(int) throw(int); virtual string fun3() throw(int,string); ; class CDerived:public CBase public: int fun1(int) throw(int);/错!异常规范不如throw()严格 int fun2(int

25、) throw(int);/对!有相同的异常规范 string fun3() throw(string);/对!异常规范比throw(int,string)更严格,10.7 C+标准库的异常类层次结构,C+标准库提供了一个异常类层次结构,用来报告C+标准库中的函数执行期间遇到的程序不正常情况。这些异常类也可以被用在用户编写的程序中,或被进一步派生来描述程序中的异常。 C+标准库中的异常层次的根类被称为exception,定义在库的头文件中,它是C+标准库函数抛出的所有异常类的基类。exception类的接口如下: namespace std/注意在名字空间域std中 class exception public: exception() throw();/缺省构造函数 exception(const exception ,10.7 C+标准库的异常类层次结构,C+标准库还提供了一些类,可用在用户编写的程序中,以报告程序的不正常情况。这些预定义的错误被分为两大类:逻辑错误(logic error)和运行时错误(run_t

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论