c++完全讲义ppt unit10.ppt_第1页
c++完全讲义ppt unit10.ppt_第2页
c++完全讲义ppt unit10.ppt_第3页
c++完全讲义ppt unit10.ppt_第4页
c++完全讲义ppt unit10.ppt_第5页
已阅读5页,还剩35页未读 继续免费阅读

下载本文档

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

文档简介

第十章异常处理 大型和十分复杂的程序往往会产生一些很难查找的甚至是无法避免的运行时错误 当发生运行时错误时 不能简单地结束程序运行 而是退回到任务的起点 指出错误 并由用户决定下一步工作 面向对象的异常处理 exceptionhandling 机制是C 语言用以解决这个问题的有力工具 函数执行时 放在try 测试 程序块中的任何类型的数据对象发生异常 都可被throw表达式抛出 随即逆调用链退回 直到被catch子句捕获 并在此执行异常处理 报告出现的异常等情况 从抛出到捕获 应将各嵌套调用函数残存在栈中的自动对象 自动变量和现场保护内容等进行清除 如果已退到入口函数还未捕获则由terminate 函数来终结入口函数 第十章异常处理 10 1异常的概念 10 3栈展开与异常捕获 10 2异常处理的机制 10 5异常和继承 10 7C 标准库异常类层次结构 选读 10 6异常规范 选读 10 4异常的重新抛出和catch all子句 10 1异常的概念 异常概念的引入 异常 exception 是程序可能检测到的 运行时不正常的情况 如存储空间耗尽 数组越界 被0除等等 可以预见可能发生在什么地方 但是无法确知怎样发生和何时发生 特别在一个大型的程序 软件 中 程序各部分是由不同的小组编写的 它们由公共接口连起来 错误可能就发生在相互的配合上 也可能发生在事先根本想不到的个别的条件组合上 C 提供了一些内置的语言特性来产生 raise 或抛出 throw 异常 用以通知 异常已经发生 然后由预先安排的程序段来捕获 catch 异常 并对它进行处理 这种机制可以在C 程序的两个无关 往往是独立开发 的部分进行 异常 通信 由程序某一部分引发了另一部分的异常 这一异常可回到引起异常的部分去处理 逆着程序函数的调用链 10 2异常处理的机制 测到栈满或空就抛出一个异常 templatevoidStack Push constT 注意pushOnFull是类 C 要求抛出的必须是对象 所以必须有 即调用构造函数建立一个对象 异常与异常抛出 以栈为例 异常类声明如下 classpopOnEmpty 栈空异常classpushOnFull 栈满异常 10 2异常处理的机制 throw表达式抛出异常为异常处理的第一步 在堆栈的压栈和出栈操作中发生错误而抛出的异常 理所当然地应由调用堆栈的程序来处理 异常并非总是类对象 throw表达式也可以抛出任何类型的对象 如枚举 整数等等 但最常用的是类对象 在C 中异常抛出与异常处理之间有一整套程序设计的机制 首先采用关键字try 构成一个try块 tryblock 它包含了抛出异常的语句 当然也可以是包含了这样的调用语句 该语句所调用的函数中有能够抛出异常的语句 异常处理机制 10 2异常处理的机制 intmain inta 9 1 2 3 4 5 6 7 8 9 b 9 0 i stackistack 8 try for i 0 i cerr cerr 栈空 endl for i 0 i 9 i cout b i t cout endl return0 try块与catch子句的关系实例 10 2异常处理的机制 由catch字句捕获并处理异常是第二步 注意与catch语句分别匹配的是在压栈和出栈成员函数模板中的throw语句 一个抛出pushOnFull类的无名对象 另一个抛出popOnEmpty类的无名对象 在编制程序时有一条惯例 把正常执行的程序与异常处理两部分分隔开来 这样使代码更易于跟随和维护 在上例中 我们可以把两个try块合成一个 而把两个catch子句都放在函数最后 说明 这里有两个try块 分别对应压栈与出栈 也有两个catch子句 catchclause 分别处理压栈时的栈满和出栈时的栈空 10 2异常处理的机制 1 如果没有异常发生 继续执行try块中的代码 与try块相关联的catch子句被忽略 程序正常执行 main 返回0 2 当第一个try块在for循环中抛出异常 则该for循环退出 try块也退出 去执行可处理pushOnFull异常的catch子句 istack PrintStack 不再执行 被忽略 3 如果第二个try块调用Pop 抛出异常 则退出for和try块 去执行可处理popOnEmpty异常的catch子句 4 当某条语句抛出异常时 跟在该语句后面的语句将被跳过 程序执行权交给处理异常的catch子句 如果没有catch子句能够处理异常 则交给C 标准库中定义的terminate 流程控制规则 10 3栈展开与异常捕获 catch子句由三部分组成 关键字catch 圆括号中的异常声明以及复合语句中的一组语句 catch子句不是函数 所以圆括号中不是形参 而是一个异常类型声明 可以是类型也可以是对象 catch子句的使用 它只有一个子句 没有定义和调用之分 使用时由系统按规则自动在catch子句列表中匹配 catch子句可以包含返回语句 return 也可不包含返回语句 包含返回语句 则整个程序结束 而不包含返回语句 则执行catch列表之后的下一条语句 catch子句说明 当try块中的语句抛出异常时 系统通过查看跟在其后的catch子句列表 来查找可处理该异常的catch子句 10 3栈展开与异常捕获 对应在throw表达式中 构造抛出对象也要有实参 throwpushOnFull data data即Push const data 中的参数data templateclasspushOnFull T value public pushOnFull Ti value i 或写为pushOnFull Ti value i Tvalue return value 新的私有数据成员 value保存那些不能被压入栈中的值 该值即调用构造函数时的实参 catch子句异常声明探讨 异常声明中可以是一个对象声明 以栈为例 当栈满时 要求在异常对象中保存不能被压入到栈中的值 pushOnFull类可定义如下 10 3栈展开与异常捕获 在catch子句中 要取得 value 须调用pushOnFull中的成员函数value catch pushOnFulleObj cerr 栈满 eObj value 未压入栈 endl return1 在catch子句的异常声明中声明了对象eObj 用它来调用pushOnFull类的对象成员函数value 异常对象是在抛出点被创建 与catch子句是否显式要求创建一个异常对象无关 该对象总是存在 在catch子句中只是为了调用异常处理对象的成员函数才声明为对象 不用类 catch子句异常声明中采用对象只是一种形式 甚至异常并非一个类对象时 也可以用同样的格式 比如异常为一枚举量 这时就等效于按值传递 而不是调用类对象的公有成员 10 3栈展开与异常捕获 catch子句的异常声明与函数参数声明类似 可以是按值传送 也可以是按引用传递 对大型类对象减少不必要的复制是很有意义的 所以对于类类型的异常 其异常声明最好也是被声明为引用 如 catch pushOnFull 使用引用类型的异常声明 catch子句能够修改异常对象 但仅仅是异常对象本身 正常程序部分的量并不会被修改 与一般类对象不同 实际上异常对象处理完后 生命期也就结束了 只有需要重新抛出异常 在下一节中讨论 修改操作才有意义 例10 1 包含栈满或空异常的完整的程序 10 3栈展开与异常捕获 把程序的正常处理代码和异常处理代码分离的最清楚的方法是定义函数try块 FunctiontryBlock 这种方法是把整个函数包括在try块中 一个函数try块把一组catch子句同一个函数体相关联 如果函数体中的语句抛出一个异常 则考虑跟在函数体后面的处理代码来处理该异常 函数try块对构造函数尤其有用 例10 1 1 定义函数try块 FunctiontryBlock 函数try块的使用 寻找匹配的catch子句有固定的过程 如果throw表达式位于try块中 则检查与try块相关联的catch子句列表 看是否有一个子句能够处理该异常 有匹配的 则该异常被处理 找不到匹配的catch子句 则在主调函数中继续查找 如果一个函数调用在退出时带有一个被抛出的异常未能处理 而且这个调用位于一个try块中 则检查与该try块相关联的catch子句列表 看是否有一个子句匹配 有 则处理该异常 没有 则查找过程在该函数的主调函数中继续进行 即这个查找过程逆着嵌套的函数调用链向上继续 直到找到处理该异常的catch子句 只要遇到第一个匹配的catch子句 就会进入该catch子句 进行处理 查找过程结束 10 3栈展开与异常捕获 在栈异常处理的例子中 对popOnEmpty 首先应在istack的成员函数Pop 中找 因为Pop 中没有try块 不存在catch子句 所以Pop 带着一个异常退出 下一步是检查调用Pop 的函数 这里是main 在main 中对Pop 的调用位于一个try块中 则可用与该try块关联的catch子句列表中的某一个来处理 找到第一个popOnEmpty类型异常声明的catch子句 并进入该子句进行异常处理 栈展开 因发生异常而逐步退出复合语句和函数定义的过程 被称为栈展开 stackunwinding 这是异常处理的核心技术 异常对程序的影响通常不仅是在发生异常的那个局部范围中 而且可能逆调用链而上 甚至整个任务 因此 异常处理应该在其对程序影响的终结处进行 甚至是在调用该任务的菜单处进行 10 3栈展开与异常捕获 在栈展开期间 在退出的域中有某个局部量是类对象 栈展开过程将自动调用该对象的析构函数 完成资源的释放 所以C 异常处理过程本质上反映的是 资源获取是由构造函数实现 而资源释放是由析构函数完成 采用面向对象的程序设计 取得资源的动作封装在类的构造函数中 释放资源的动作封装在类的析构函数中 当一个函数带着未处理的异常退出时 函数中这种类对象被自动销毁 资源 包括动态空间分配的资源和打开的文件 释放 所以由文件重构对象应该放在构造函数中 把对象存入文件应该放在析构函数中 栈展开时资源的释放 异常处理应该用于面向对象的程序设计 对非面向对象的程序设计如果函数动态获得过资源 因异常 这些资源的释放语句可能被忽略 则这些资源将永远不会被自动释放 10 3栈展开与异常捕获 异常不能够保持在未被处理的状态 异常表示一个程序不能够继续正常执行 这是非常严重的问题 如果没有找到处理代码 程序就调用C 标准库中定义的函数terminate 异常对象的探讨 异常对象是在throw表达式中建立并抛出 throw表达式通过调用异常类的构造函数创建一个临时对象 然后把这个临时对象复制到一个被称为异常对象 exceptionobject 的存贮区中 它保证会持续到异常被处理完 10 3栈展开与异常捕获 函数调用和异常处理的区别 建立函数调用所需要的全部信息在编译时已经获得 而异常处理机制要求运行时的支持 对于普通函数调用 通过函数重载解析过程 编译器知道在调用点上哪个函数会真正被调用 但对于异常处理 编译器不知道特定的throw表达式的catch子句在哪个函数中 以及在处理异常之后执行权被转移到哪儿 这些都在运行时刻决定 异常是随机发生的 异常处理的catch子句是逆调用链进行查找 这与运行时的多态 虚函数也是不一样的 当一个异常不存在处理代码时 系统无法通知用户 所以要有terminate 函数 它是一种运行机制 当没有处理代码 catch子句 能够匹配 被抛出的异常时由它通知用户 10 4异常的重新抛出和catch all子句 选读 rethrow表达式仍为 throw 但仅有一个关键字 因为异常类型在catch语句中已经有了 不必再指明 被重新抛出的异常就是原来的异常对象 但是重新抛出异常的catch子句应该把自己做过的工作告诉下一个处理异常的catch子句 往往要对异常对象做一定修改 以表达某些信息 因此catch子句中的异常声明必须被声明为引用 这样修改才能真正做在异常对象自身中 而不是副本中 异常的重新抛出与连续处理 当catch语句捕获一个异常后 可能不能完全处理异常 完成某些操作后 该异常必须由函数链中更上级的函数来处理 这时catch子句可以重新抛出 rethrow 该异常 把异常传递给函数调用链中更上级的另一个catch子句 由它进行进一步处理 10 4异常的重新抛出和catch all子句 选读 通用catch子句 catch all catch 代码 任何异常都可以进入这个catch子句 这里的三个点称为省略号 花括号中的复合语句用来执行指定操作 异常发生后按栈展开 stackunwinding 退出 动态分配的非类对象资源不会自动释放的 通常在catch all子句中释放 voidfun1 int res res newint 100 定义一个资源对象try 代码包括使用资源res和某些可能引起异常抛出的操作 异常可能有多种catch 不论是那种异常都在此释放delete res 释放资源对象resthrow 重新抛出异常delete res 正常退出前释放资源对象res 10 4异常的重新抛出和catch all子句 选读 catch all子句可以单独使用 也可以与其它catch子句联合使用 如果联合使用 它必须放在相关catch子句表的最后 catch子句被检查的顺序与它们在try块之后排列顺序相同 一旦找到了一个匹配 则后续的catch子句将不再检查 按此规则 catch all子句 catch 处理表前面所列各种异常之外的异常 如果只用catch all子句进行某项操作 则其他的操作应由catch子句重新抛出异常 逆调用链去查找新的处理子句来处理 不能在子句列表中再安排一个处理同一异常的子句 因为第二个子句是永远执行不到的 通用catch子句的应用 10 5异常和继承 定义一个称为Excp的基类 由它来打印错误信息 classExcp public voidprint stringmsg cerr msg endl 再从该基类派生出两个异常类 classstackExcp publicExcp 栈异常类的基类classmathExcp publicExcp 数学库异常的基类进一步派生出其他异常类 classpopOnEmpty publicstackExcp 栈空退栈异常classpushOnFull publicstackExcp 栈满压栈异常classzeroOp publicmathExcp 数学库零操作异常classdivideByZero publicmathExcp 数学库被零除异常 异常的层次结构 在C 程序中 表示异常的类通常被组成为一个组 即如在前面各节讨论的那样 或者一个层次结构 形成了三层结构 10 5异常和继承 在层次结构下 异常的抛出会有一些不同 以下做法是错的 if full pushOnFullexcept data stackExcp pse 抛出的异常对象的类型为stackExcp 这里被创建的异常类对象是stackExcp类类型 尽管pse指向一个实际类型为pushOnFull的对象 但那是一个临时对象 复制到异常对象的存储区中时创建的却是stackExcp类的异常对象 所以该异常不能被pushOnFull类型的catch子句处理 层次结构异常的抛出 10 5异常和继承 在处理类类型异常时 catch子句的排列顺序是非常重要的 catch pushOnFull 处理pushOnFull异常catch stackExcp 处理栈的其他异常catch Excp 处理一般异常派生类类型的catch子句必须先出现 以确保只有在没有其他catch子句适用时 才会进入基类类型的catch子句 异常catch子句不必是与异常最匹配的catch子句 而是最先匹配到的catch子句 就是第一个遇到的可以处理该异常的catch子句 所以在catch子句列表中最特化的 匹配条件最严格的 catch子句必须先出现 catch子句的排列顺序 10 5异常和继承 类层次结构的异常同样可以重新抛出 rethrow 把一个异常传递给函数调用列表中更上层的另一个catch子句 throw 类层次结构下的异常重新抛出 重新抛出的异常仍是原来的异常对象 如果程序中抛出了pushOnFull类类型的异常 而它被基类的catch子句处理 并在其中再次被抛出 那么这个异常仍是pushOnFull类的异常 而不是其基类的异常 10 5异常和继承 在基类catch子句处理的是异常对象的基类子对象的一份副本 该副本只在该catch子句中被访问 重新抛出的是原来的异常对象 这个放在异常对象存储区中的异常的生命期应该是在处理该异常的一系列的子句中最后一个退出时才结束 也就是直到这时 才由异常类的析构函数来销毁它 这一系列的子句是由重新抛出联系起来的 例10 2 异常层次结构中的虚函数 为了调用派生类对象的虚拟函数 异常声明必须为一个指针或引用 虚函数是类层次结构中多态性的基本手段 异常类层次结构中也可以定义虚拟函数 10 6异常规范 一个函数的异常规范的违例只能在运行时才能被检测出来 如果在运行时 函数抛出了一个没有被列在它的异常规范中的异常时 并且函数中所抛出的异常 没有在该函数内部处理 则系统调用C 标准库中定义的函数unexpected 仅当函数中所抛出的异常 没有在该函数内部处理 而是逆调用链回溯寻找匹配的catch子句的时候 异常规范才起作用 如果异常规范为throw 则表示不得抛出任何异常 异常规范 exceptionspecification 提供了一种方案 可以随着函数声明列出该函数可能抛出的异常 并保证该函数不会抛出任何其他类型的异常 在stack类定义中可有 voidPush constT成员函数类内声明和类外定义必须必须在两处都有相同的异常规范 同样的异常规范 异常规范的引入 10 6异常规范 classCBase public virtualintfun1 int throw virtualintfun2 int throw int virtualstringfun3 throw int string classCDerived publicCBase public intfun1 int throw int 错 异常规范不如throw 严格intfun2 int throw int 对 有相同的异常规范stringfun3 throw string 对 异常规范比throw int string 更严格 虚函数中的异常规范 派生类的虚拟函数的异常规范必须与基类虚函数的异常一样或更严格 因为当派生类的虚函数被指向基类类型的指针调用时 保证不会违背基类成员函数的异常规范 10 7C 标准库的异常类层次结构 选读 exception类的接口如下 namespacestd 注意在名字空间域std中classexception public exception throw 默认构造函数exception constexception C 标准库中的异常层次的根类被称为exception 定义在库的头文件中 10 7C 标准库的异常类层次结构 选读 C 标准库提供的逻辑异常 invalid argument异常 接收到一个无效的实参 抛出该异常 out of range异常 收到一个不在预期范围中的实参 则抛出 length error异常 报告企图产生 长度值超出最大允许值 的对象domain error异常 用以报告域错误 domainerror C 标准库提供的运行时异常 range error异常 报告内部计算中的范围错误 overflow error异常 报告算术溢出错误 underflow error异常 报告算术下溢错误 以上三个异常是由runtime error类派生的 bad alloc异常 当new 操作符不能分配所要求的存储区时 会抛出该异常 它是由基类exception派生的 例10 3 为类模板Array重新定义下标操作符 如果索引值越界 它会抛出一个out of range类型的异常 第十章异常处理 完 谢谢 例10 1 堆栈异常处理 templateclasspushOnFull 栈满异常声明T value public pushOnFull Ti value i Tvalue return value voidprint cerrclasspopOnEmpty 栈空异常声明public voidprint cerr 栈已空 无法出栈 endl 例10 1 堆栈异常处理 templateclassStack inttop 栈顶指针 下标 T elements 动态建立的数值intmaxSize 栈最大允纳的元素个数public Stack int 20 栈如不指定大小 设为2

温馨提示

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

评论

0/150

提交评论