
九讲——异常处理.ppt
76页第十二章 异常处理,C++语言程序设计,本章主要内容,异常处理的基本思想C++异常处理的实现异常处理中的构造与析构,编译错编译时通不过,属语法错,最浅层次的错误逻辑错设计缺陷,编译器无法发现,只能靠人工分析跟踪排除,错误层次中等运行错调试时无法发现,运行时才出现,往往由系统环境引起,属可预料但不可避免必须由语言的某种机制予以控制注意,调试出错和运行出错时机不同,是两种不同性质的错误错误分类:,程序逻辑中不应出现的情况叫“非法”它不应包含在正常程序代码中如对一个字符串拷贝函数传入空串指针,调试时要用assent()拦截在程序运行中出现的、属正常逻辑,但一旦出现会导致程序瘫痪,叫“出错”比如在读某文件之前,要先打开文件,打开失败不能算“非法”,只算“出错”分清“非法”与“出错”,一天,某公司派小张去机场提货,派小王去银行存款机场仓库失火,小张没提出货,也没回公司报告,原来一根筋的他在一边看热闹,可公司上下急得团团转在去银行路上,小王遇到抢匪打劫,小王立即报警,并报告了公司马上协助警方追捕抢匪,追回存款,再送银行完成了存款任务假设,你是公司经理,你需要什么样的员工?,为何需要“异常处理”,对于可预料但不可避免的程序错误,不能眼睁睁看着它发生而无所作为(只会用exit()),要将消极等待变为积极预防,还要将预防的处理内容归纳整理,分门别类地作成类。
积极之意是,不能只凭编程经验这种个体性偶然性的做法,而要凭借人人都掌握的规范圆满的处理这规范是指在函数的处理中设下陷阱,一旦触发异常,定会被异常处理所收容,统一归口处理异常处理的指导思想,默认的程序中断执行特制的——用“异常处理”转而执行特制的处理函数 错误处理的做法:,两个人不幸得了病,他们得到不同的处置:exit()不分青红皂白,马上杀了埋掉异常处理”马上送去医院就诊,尽力治疗,可能痊愈出院,也可能经全力抢救还是无力回天,撒手人间 两种做法,孰优孰劣,一目了然exit() 与异常处理的比较,允许从异常抛出点把任意数量的信息以类型安全的方式传给异常处理器(catch);对于没有抛出异常的代码段,不应有任何额外的系统(时间、空间)开销;应保证所抛出的任何异常都能被适当的处理器捕获;通过语言提供的某种语法,程序员可以写出自己的异常处理器;能够直接应用到多线程程序中; ...,异常处理的追求目标— Stroustrop,异常处理机制实际上是一种运行时通知机制,而非程序预设机制异常处理机制发生在错误出现之前(异常条件一旦符合,立即触发“抛掷”)而C语言的出错处理机制则是,当错误发生之后所采取的补救/罢工措施。
防患于未然,成功避险,显然优于事后补救一个大型软件,其功能是通过底层调用来实现的,错误大都发生在底层你总不能仅仅提示“数据溢出”、“进程失败”之类的信息而应当“翻译”成“账户余额不足,不能转帐”、“燃油不足,发动机即将关闭”之类而上层组件如何知道下层发生了什么错误?当然要底层代码的报告,报告的方法就是抛出异常对象由于c/c++是函数性语言,函数的调用是嵌套的,异常的发生地和处理地就很可能不在同一个模块中,逻辑上讲应是嵌套的;异常一旦发生,则意味着后续语句不可能再执行了异常处理的基本认识,如有一函数其调用式为: sin ( 2a/sqrt((x+y)/c)),,,,,,,,,sin,sqrt,2*a,x+y,/c,,抛掷错(拦截),域,异常处理的基本思想,异常处理的实现机制,抛掷异常的程序段......throw 表达式;......,捕获并处理异常的程序段try 复合语句catch(异常类型声明) 复合语句catch(异常类型声明) 复合语句 …,,,相呼应的三个关键字:try throw catch,这三个关键字分工明确,各司其职:throw负责发现异常的报警、抛掷异常对象;若放在函数声明中则又称为异常接口声明;try设置了一个侦错范围,又叫保护段,是个区域。
其实是划定了一个跳跃的边界;catch负责处理捕获来的异常(包括继续抛掷异常)异常处理的语法结构,每个catch()相当于一段函数代码;每个throw则相当于一个函数调用;每个try块至少跟一个catch();一个程序可设置个数不定的try 、throw 和 catch它们只有逻辑上的呼应,而无数量上的对应关系,且不受所在函数作用域限制;异常抛掷点往往距异常捕获点很远,它们可以不在同层模块中;甚至有的throw我们看不到在哪,实际上在我们所调用的系统函数中,在标准库中;程序中try块可以并列、可以嵌套;catch子句的类型匹配靠C++的RTTI技术throw和catch的呼应机制是靠类型匹配实现的 throw只管抛出异常的类型,至于由哪个catch接着,那是匹配机制的事由于异常处理机制是按类型匹配的,因此catch的参数可以没有参数名,除非你要使用参数做别的事情而且只能有一个参数是将检测与处理分离,以便各司其职、灵活搭配地工作它们的联系靠类型任何类型都可以作为异常类型,包括基本类型或扩展类型,甚至空类型所抛掷的异常对象并非建在函数栈上,而是建在专用的异常栈上,故可以跨越函数而传递给上层。
因此也不要将函数的局部对象的地址或引用作为异常对象抛出,因为局部对象将随着函数栈的清除而消失异常处理机制的图示,17,,,,,,,,,,普通函数,catch,try,throw,,main(),,,,,try块内函数,try,,,catch,异常处理的实现机制,若有异常则通过throw创建一个异常对象并抛掷将可能抛出异常的程序段嵌在try块之中按正常的顺序执行到达try语句,然后执行try块内的保护段如果在保护段执行期间没有引起异常,那么跟在try块后的catch子句就不执行程序从try块后跟随的最后一个catch子句后面的语句继续执行下去catch子句按其在try块后出现的顺序被匹配选中的catch将捕获并处理异常(或继续抛掷异常)如果匹配的处理器未找到,则运行函数terminate将被自动调用,其缺省功能是调用abort终止程序例12-1处理除零异常,#include 以弥补基本类型的能力不足和信息不足程序运行结果如下:that is ok.,C++中对抛出的异常是按排列次序匹配的catch处理程序的出现顺序很重要,因为在一个try块中,异常处理程序是按照它出现的顺序被检查的只要找到一个匹配的异常类型,后面的异常处理都将被忽略例如,在下面的异常处理块中,首先出现的是catch(...),它可以匹配任何异常,在任何情况下,其它的catch语句都被略过因此,catch(...)应该放在最后catch的匹配次序,//...try{ //...}catch(...){ //只在这里处理所有的异常}//错误:后面的异常处理程序段不会被检查到catch(const char* str){ … },关于throw抛掷的异常对象,若throw抛掷了异常对象,则该对象应该是非局部对象本身,也非该对象的引用,并且非指向该对象的指针它应该是一个叫做异常对象的东西因为局部对象是短命的try——throw——catch实际是一种程序控制机制,当其中的throw语句一旦被触发,就会将执行流引到其后的catch去匹配只要找到一个匹配的异常类型,其它的异常处理都将被忽略。 当异常处理块被执行完后,会找到本层catch之后的语句去执行,try中被throw 跳过的语句不会再被执行 这种机制与程序三大控制结构很类似,只不过是非正常情况下的一种控制机制异常处理的语法机制,与异常处理相关的四个流程,1. 没有异常抛出时的正常执行流程;2. 抛掷了异常,且在当前函数范围内被捕获着了的执行流程;3. 抛掷了异常,但在当前函数范围内没有被捕获着的执行流程;4. 异常被处理后的执行流程C对出错的处理是将所有错误予以编码(返回码),一码一错程序员编程时,通过在主调函数中检查出现的返回码,给与相应的处理这种方法繁琐又不规范,更致命的是,这种“事后处理”的机制会使一切抢救都为时已晚C++则是将形形色色的错误归结抽象为“类型错”,统统交给throw抛掷;将出错处理统一为catch 调用这就为程序员编程制定了统一的规范,也就是运行协议更重要的是, C++的出错处理机制是“拦截”,然后做抢救处理能抢救过来,则救活了,因此属“防患于未然”异常处理”实际是个动态概念其处理机制是靠类型匹配,这恰是运行时才能发生的事C++的异常处理机制有何特色?,C对出错处理的返回码机制 ,与C++的异常处理机制有何不同?1。 C将正常逻辑与异常逻辑混在一起每个函数调用后都要用正常逻辑(如if)加以判断,以过滤出异常这样使子函数的错误扩散到了其所有的调用者中,形成了一个混杂的怪异链增加了用于处理很少出现的特殊情况的运行费用尤其对于C++,其成员函数通常只有几行代码,但函数很多,若每个函数都使用返回码技术,则臃肿不堪类中还有构造、拷贝构造等无返回值的函数,它们出错处理的权力不应被剥夺返回码靠的是预定的数字代号来传递信息,所能携带的信息量少且呆板,远少于抛掷的对象所携带的信息量异常处理的不唤醒机制,即一旦在保护段执行期间发生异常,则立即throw抛掷,由相应的catch子句捕获处理抛掷 ——捕获之间的代码被越过而不执行程序从try块后跟随的catch子句最后一个后面的语句继续执行下去throw越过的语句不再执行;catch执行之后也不再返回异常处理是一种C++新增的程序控制机制(还记得原有的九种控制机制吗?),也不同于函数调用机制(函数属唤醒机制)当不唤醒机制与以上其他机制交织在一起时,具有最高的优先级。






![河南新冠肺炎文件-豫建科[2020]63号+豫建科〔2019〕282号](http://img.jinchutou.com/static_www/Images/s.gif)





