C程序设计课件(第5章).ppt
第第5章章异常处理异常处理n本章学习重点掌握内容:本章学习重点掌握内容:n异常的概念、异常的产生异常的概念、异常的产生n异常的处理机制异常的处理机制nthrow、try和和catch的用法的用法n捕捉所有的异常捕捉所有的异常n异常信号的传递方式异常信号的传递方式n标准标准C+库的异常类库的异常类1/16/20231第第5章章异常处理异常处理n5.1异常的概念异常的概念n5.2异常处理机制异常处理机制n5.3没有被捕捉的异常没有被捕捉的异常n5.4catch(.)使用)使用n5.5用类的对象传递异常用类的对象传递异常n5.6标准标准C+库中的异常类库中的异常类n5.7综合应用实例综合应用实例1/16/202325.1异常的概念异常的概念5.1.1异常的概念异常的概念程序运行过程中,由于环境变化、用户操程序运行过程中,由于环境变化、用户操作失误以及其它方面的原因而产生的运行时不作失误以及其它方面的原因而产生的运行时不正常的情况,它要求程序立即进行处理,否则正常的情况,它要求程序立即进行处理,否则将会引起程序错误甚至崩溃的现象。将会引起程序错误甚至崩溃的现象。常见的异常有:空闲内存耗尽、请求打开常见的异常有:空闲内存耗尽、请求打开不存在的文件、被不存在的文件、被0除、打印机未打开、数组除、打印机未打开、数组越界访问等。越界访问等。1/16/202335.1.2异常的产生异常的产生C+程序是由一些相互分离的模块组成的,程序程序是由一些相互分离的模块组成的,程序中出现错误和解决出现的错误就会分成两个部分中出现错误和解决出现的错误就会分成两个部分:(1)某个模块)某个模块A出现错误,但它并没有能力在模出现错误,但它并没有能力在模块块A内解决这个错误,因此它就给出关于这个错内解决这个错误,因此它就给出关于这个错误的报告。误的报告。(2)某个模块)某个模块B能够检测到模块能够检测到模块A发出的错误报发出的错误报告,并处理这个错误,使出现错误造成的损失减告,并处理这个错误,使出现错误造成的损失减到最小。到最小。1/16/202345.2异常处理机制异常处理机制5.2.1基本概念基本概念1抛出异常抛出异常如果程序发生异常情况,而在当前的上下文环如果程序发生异常情况,而在当前的上下文环境中获取不到处理这个异常的足够信息,程序境中获取不到处理这个异常的足够信息,程序将创建一个包含出错信息的对象并将该对象抛将创建一个包含出错信息的对象并将该对象抛出当前上下文环境,将错误信息发送到更大的出当前上下文环境,将错误信息发送到更大的上下文环境中,这个过程称为抛出(上下文环境中,这个过程称为抛出(throw)异常。异常。1/16/202355.2.1基本概念基本概念2捕捉异常捕捉异常对于一个抛出的异常,如果某一个模块能够对于一个抛出的异常,如果某一个模块能够(或想要)处理这个异常,它就可以获得程序的(或想要)处理这个异常,它就可以获得程序的控制权处理该异常,这个过程称为捕捉控制权处理该异常,这个过程称为捕捉(catch)异常。)异常。3处理异常处理异常当某个当某个catch块捕捉到异常后,它就根据事先制块捕捉到异常后,它就根据事先制定的策略对异常进行处理,这就是处理异常。在定的策略对异常进行处理,这就是处理异常。在C+中,只有中,只有catch块能够捕获异常并进行处理,块能够捕获异常并进行处理,因此因此catch块又称为异常处理器。块又称为异常处理器。1/16/202365.2.1基本概念基本概念4C+的异常处理机制的异常处理机制C+的异常处理机制就是将抛出异常与捕捉的异常处理机制就是将抛出异常与捕捉异常、处理异常分离开来。抛出异常的模块并异常、处理异常分离开来。抛出异常的模块并不负责异常的处理,它只是报告某个地方存在不负责异常的处理,它只是报告某个地方存在错误,这个报告可以帮助异常处理器解决这个错误,这个报告可以帮助异常处理器解决这个错误。而异常处理器则根据抛出异常模块的报错误。而异常处理器则根据抛出异常模块的报告来处理异常,如果没有模块抛出异常,就不告来处理异常,如果没有模块抛出异常,就不会有异常的处理。会有异常的处理。1/16/202375.2.2throw语句语句抛出异常的语法格式如下:抛出异常的语法格式如下:throw表达式表达式这里,这里,throw后的表达式表示异常的类型,后的表达式表示异常的类型,它可以是一个变量或一个对象。它可以是一个变量或一个对象。throw语句在语句在语法上与语法上与return语句相似。下面是两条语句相似。下面是两条throw语句的例子。语句的例子。throw1;throw(“出现异常出现异常”);异常抛出后,程序的控制权就从异常抛出的异常抛出后,程序的控制权就从异常抛出的地方交出,由编译器寻找匹配的异常处理器地方交出,由编译器寻找匹配的异常处理器进行相应的处理。进行相应的处理。1/16/202385.2.3try块块try块的语法格式如下:块的语法格式如下:try复合语句复合语句try块必须包围能够抛出异常的语句。它提示编块必须包围能够抛出异常的语句。它提示编译器到那里查找异常处理器,没有跟在译器到那里查找异常处理器,没有跟在try块块后的后的catch块是没有用的。块是没有用的。try块可以包含任何块可以包含任何C+语句,甚至包含整个语句,甚至包含整个函数。函数。1/16/202395.2.4catch块块catch块的语法格式如下。块的语法格式如下。catch(异常类型声明异常类型声明)异常处理语句异常处理语句catch(异常类型声明异常类型声明)异常处理语句异常处理语句1/16/2023105.2.4catch块块ncatch后括号中的异常类型声明可以是一个类后括号中的异常类型声明可以是一个类型或一个对象声明,后边一对型或一个对象声明,后边一对“”括住的是括住的是一组复合语句。一个一组复合语句。一个catch块相当于一个以类块相当于一个以类型为单一参数的函数。型为单一参数的函数。ncatch块必须直接放在块必须直接放在try块之后。块之后。catch语句语句与与switch语句不同,它不需要在每个语句不同,它不需要在每个case语语句后加入句后加入break用以中断后面程序的执行。用以中断后面程序的执行。n一个一个catch块引入一个局部域,在块引入一个局部域,在catch块内块内声明的变量不能在声明的变量不能在catch块外引用。块外引用。1/16/202311【例【例5.2】局部域声明的变量不能被局部域外引用例题。】局部域声明的变量不能被局部域外引用例题。#includevoidmain()try/一段可能引起异常的代码一段可能引起异常的代码throw(出现异常!出现异常!);catch(char*message)inty=1;cout处理了处理了char*类型的异常类型的异常endl;coutyendl;/编译错误,变量编译错误,变量y未定义未定义1/16/2023125.2.5异常处理模式异常处理模式C+的异常处理有两种基本模式:的异常处理有两种基本模式:1终止模式终止模式异常抛出后,捕捉异常并退出导致异常的子程异常抛出后,捕捉异常并退出导致异常的子程序或子系统,退出需要关闭适当的文件,析构序或子系统,退出需要关闭适当的文件,析构适当的对象,释放适当的内存,处理需要处理适当的对象,释放适当的内存,处理需要处理的设备等,这种方法称为终止模式。的设备等,这种方法称为终止模式。缺省情况下,缺省情况下,C+异常处理机制采用终止方异常处理机制采用终止方法。法。【例【例5.3】捕捉异常后直接退出的例题。】捕捉异常后直接退出的例题。1/16/202313#includevoidfunc2()/一段有可能引起异常的代码一段有可能引起异常的代码throw1;cout其它程序语句!其它程序语句!endl;voidmain()try1/16/202314func2();/func2()抛出的异常值为抛出的异常值为1/程序抛出异常后的语句部分程序抛出异常后的语句部分cout异常处理结束后继续执行!异常处理结束后继续执行!endl;catch(intx)/对异常的处理对异常的处理cout处理了处理了int类型的异常类型的异常endl;cout程序结束!程序结束!endl;在采用终止模式情况下程序的运行结果为:在采用终止模式情况下程序的运行结果为:处理了处理了int类型的异常!类型的异常!程序结束!程序结束!1/16/2023155.2.5异常处理模式异常处理模式2恢复模式恢复模式异常抛出后,捕捉异常并试图去纠正或调异常抛出后,捕捉异常并试图去纠正或调整引起异常的条件,然后从发生异常的地方继整引起异常的条件,然后从发生异常的地方继续执行,这种方法称为恢复模式。续执行,这种方法称为恢复模式。恢复模式实现起来非常困难,在实际应用恢复模式实现起来非常困难,在实际应用中,除了一些特殊的领域外,一般都不采用恢中,除了一些特殊的领域外,一般都不采用恢复模式处理异常。复模式处理异常。1/16/2023165.2.6重新抛出重新抛出n在异常处理过程中也可能存在在异常处理过程中也可能存在“单个单个catch子子句不能完全处理这个异常句不能完全处理这个异常”的情况。那么该异的情况。那么该异常处理器在做完局部能够做的事情后,会再一常处理器在做完局部能够做的事情后,会再一次抛出这个异常,让函数调用链中更上级的函次抛出这个异常,让函数调用链中更上级的函数来处理,这个过程称作重新抛出数来处理,这个过程称作重新抛出(rethrow)n重新抛出的语法形式如下:重新抛出的语法形式如下:throw;n重新抛出的还是原来捕捉到的那个异常。重新重新抛出的还是原来捕捉到的那个异常。重新抛出只能出现在抛出只能出现在catch块中。块中。1/16/202317【例【例5.4】重新抛出捕捉的异常例题。】重新抛出捕捉的异常例题。#includevoidfunc3(intx)try/一段有可能引起异常的代码一段有可能引起异常的代码throwx;catch(intx)/如果异常参数如果异常参数x=0则进行处理,否则继续抛出则进行处理,否则继续抛出if(x=0)/对异常的处理对异常的处理elsecout重新抛出异常!重新抛出异常!endl;1/16/202318throw;/重新抛出重新抛出voidmain()tryfunc3(1);/程序其它部分程序其它部分catch(intx)/对异常的处理对异常的处理cout处理了处理了int类型的异常!类型的异常!endl;1/16/202319n程序运行结果为:程序运行结果为:重新抛出异常!重新抛出异常!处理了处理了int类型的异常!类型的异常!n有的情况下,异常处理器在重新抛出之前会对有的情况下,异常处理器在重新抛出之前会对异常信号进行一些修改,这个修改能够影响更异常信号进行一些修改,这个修改能够影响更高级函数调用链中的异常处理器对该异常的处高级函数调用链中的异常处理器对该异常的处理。理。5.2.6重新抛出重新抛出1/16/2023205.2.7异常规范异常规范n异常规范规定:随着函数声明列出该函数可能抛出的异常,异常规范规定:随着函数声明列出该函数可能抛出的异常,并保证该函数不会抛出其它类型的异常。常见附带异常说明并保证该函数不会抛出其它类型的异常。常见附带异常说明的函数说明有以下的函数说明有以下3种情况。种情况。(1)函数返回类型)函数返回类型函数名函数名(参数列表参数列表)throw(类型列表类型列表);(2)函数返回类型)函数返回类型函数名函数名(参数列表参数列表)throw();(3)函数返回类型)函数返回类型函数名函数名(参数列表参数列表);第一种情况,函数列出所有可能抛出的异常类型;第二种情第一种情况,函数列出所有可能抛出的异常类型;第二种情况表示函数不会抛出任何类型的异常;第三种情况表示函数况表示函数不会抛出任何类型的异常;第三种情况表示函数可能抛出任何类型的异常。可能抛出任何类型的异常。n异常规范并非强制规定,因此,没有在函数说明后附带异常异常规范并非强制规定,因此,没有在函数说明后附带异常说明并非语法错误。说明并非语法错误。1/16/202321【例【例5.6】异常规范的处理例题】异常规范的处理例题voidfunc5(intx)throw(int,char*)/x等于等于0抛出抛出int型异常,型异常,x小于小于0抛出抛出char*型异常,型异常,x大于大于0/什么也不做什么也不做if(x=0)throw0;if(x0)throwerror;voidfunc6()throw()/本函数完成本函数完成10个指令周期的延时,不抛出任何异常个指令周期的延时,不抛出任何异常inti=0;while(i10)i+;1/16/2023225.2.7异常规范异常规范有时,函数可能抛出没有列入异常规范的异常,有时,函数可能抛出没有列入异常规范的异常,出现这种情况时,系统分两种情况进行处理。出现这种情况时,系统分两种情况进行处理。(1)在函数内部(包括抛出异常的函数以及调用)在函数内部(包括抛出异常的函数以及调用该函数的函数链中的任意函数)捕捉到了这个异该函数的函数链中的任意函数)捕捉到了这个异常,进行了处理,则程序可以继续执行。常,进行了处理,则程序可以继续执行。(2)异常被抛到函数外部,系统会调用)异常被抛到函数外部,系统会调用C+标标准库中定义的函数准库中定义的函数unexpected(),该函数的缺,该函数的缺省行为是调用省行为是调用terminate(),终止程序的运行。,终止程序的运行。当然,在当然,在C+中,可以改变中,可以改变unexpected()的缺的缺省行为。省行为。1/16/2023235.3没有被捕捉的异常没有被捕捉的异常n根据异常匹配的规则,如果根据异常匹配的规则,如果try块后面的块后面的所有的所有的catch块都没有与某一异常相匹配,块都没有与某一异常相匹配,这时内层对异常的捕获失败,异常将进这时内层对异常的捕获失败,异常将进入更高层的上下文环境中进行匹配,这入更高层的上下文环境中进行匹配,这个过程一直进行直到在某个层次异常处个过程一直进行直到在某个层次异常处理器与该异常相匹配,这时才认为捕获理器与该异常相匹配,这时才认为捕获了这个异常。了这个异常。1/16/2023245.3没有被捕捉的异常没有被捕捉的异常n如果任意层的异常处理器都没有捕获到如果任意层的异常处理器都没有捕获到这个异常,那么这个异常最终会抛给这个异常,那么这个异常最终会抛给main()函数,如果在函数,如果在main()中还没有找到合中还没有找到合适的匹配,则称这个异常是适的匹配,则称这个异常是“未捕捉的未捕捉的”或或“未处理的未处理的”。如果一个异常未被。如果一个异常未被捕捉,就会调用函数捕捉,就会调用函数terminate(),终,终止本程序的运行。止本程序的运行。1/16/202325【例【例5.7】未被捕捉的异常处理例题】未被捕捉的异常处理例题#includevoidfunc7()throw0;voidmain()tryfunc7();catch(doubled)cout进行了异常处理!进行了异常处理!endl;1/16/202326n例子例子5.7中,中,func7()函数中抛出了函数中抛出了int型的异型的异常,最后抛给了常,最后抛给了main()函数,在函数,在main()函数函数中也没找到合适的匹配,于是终止本程序运行,中也没找到合适的匹配,于是终止本程序运行,main()函数函数catch块后边的程序其它部分不再执行。块后边的程序其它部分不再执行。n如果在所有函数之外的代码出现异常,比如全如果在所有函数之外的代码出现异常,比如全局对象的构造和析构等,如果有相应的异常处局对象的构造和析构等,如果有相应的异常处理器捕捉到抛出的异常,则异常处理后继续理器捕捉到抛出的异常,则异常处理后继续main()函数的执行,如果没有捕捉到抛出的异常,函数的执行,如果没有捕捉到抛出的异常,则终止本程序运行。则终止本程序运行。5.3没有被捕捉的异常没有被捕捉的异常1/16/2023275.4catch(.)使用)使用nC+在异常处理中提供了一个能捕捉所有异在异常处理中提供了一个能捕捉所有异常的常的catch块。块。catch块的语法格式如下:块的语法格式如下:catch()异常处理语句异常处理语句n其中,列表中的其中,列表中的“”表示可捕获所有的异常,表示可捕获所有的异常,但使用省略号就不可能有参数,也不可能知道所但使用省略号就不可能有参数,也不可能知道所接受到的异常为何种类型。其它部分和普通接受到的异常为何种类型。其它部分和普通catch块完全一样。块完全一样。1/16/202328【例【例5.9】使用】使用catch(.)语句的异常处理例题语句的异常处理例题#includevoidfunc5(intx)throw(int)/x等于等于0抛出抛出int型异常,型异常,x小于小于0抛出抛出char*型异常型异常if(x=0)throw0;if(x0)throwerror;voidmain()tryintx=0;coutx;1/16/202329func5(x);/程序其它部分程序其它部分catch(.)/对异常的处理对异常的处理cout处理了所有类型的异常!处理了所有类型的异常!endl;cout程序结束!程序结束!endl;程序运行结果如下:程序运行结果如下:请输入一个请输入一个int类型数据:类型数据:0处理了所有类型的异常!处理了所有类型的异常!1/16/2023305.5用类的对象传递异常用类的对象传递异常n异常信息传递是指将异常信息传递是指将throw语句抛出的异常参数语句抛出的异常参数传递到传递到catch块中。但在实际应用中,由于抛出异块中。但在实际应用中,由于抛出异常信息的需要,经常使用类的对象传递异常。使常信息的需要,经常使用类的对象传递异常。使用对象传递异常还有以下用对象传递异常还有以下2个好处:个好处:(1)在)在C+中,很好地实现了中,很好地实现了RTTI(Run-TimeTypeInformation)技术,使用对象传递)技术,使用对象传递异常,可以很好地完成异常对象的类型匹配。异常,可以很好地完成异常对象的类型匹配。(2)在)在C+中,很好地实现了对象的构造、销中,很好地实现了对象的构造、销毁、转存复制等技术,可以很好的实现异常信息毁、转存复制等技术,可以很好的实现异常信息的传递、修改和销毁等。的传递、修改和销毁等。n同函数参数传递方式一样,异常参数的传递有同函数参数传递方式一样,异常参数的传递有3种种方式:传值方式、引用方式和指针方式方式:传值方式、引用方式和指针方式1/16/2023315.5.1传值方式传递异常对象传值方式传递异常对象n按传值方式传递异常对象时,被抛出的异常都按传值方式传递异常对象时,被抛出的异常都是局部变量,而且是临时的局部变量。也就是是局部变量,而且是临时的局部变量。也就是说,每当在说,每当在throw语句抛出一个异常对象时,语句抛出一个异常对象时,不管构造的对象是什么性质的变量,此时它都不管构造的对象是什么性质的变量,此时它都会复制一份临时局部变量。会复制一份临时局部变量。【例【例5.10】按传值方式传递异常对象例题。】按传值方式传递异常对象例题。#include#includeusingnamespacestd;1/16/202332classCMyException/异常类,该类的对象作为抛出异常时传递的异常参数。异常类,该类的对象作为抛出异常时传递的异常参数。public:CMyException(stringn=none):name(n)/根据参数根据参数n构造一个名字为构造一个名字为n的异常类对象的异常类对象cout“构造一个构造一个CMyException对象,名称对象,名称为:为:nameendl;CMyException(constCMyException&e)/根据参数根据参数e拷贝构造一个异常类对象拷贝构造一个异常类对象name=e.name;cout“拷贝一个拷贝一个CMyException对象,名称对象,名称为:为:nameendl;1/16/202333virtualCMyException()cout“销毁一个销毁一个CMyException对象,名称对象,名称为:为:nameendl;stringGetName()returnname;protected:stringname;/异常类对象的名字异常类对象的名字;voidmain()try/构造一个异常对象,这是个局部变量。构造一个异常对象,这是个局部变量。CMyExceptionobj1(obj1);1/16/202334/下面抛出异常对象。注意:这时下面抛出异常对象。注意:这时VC编译器会复制编译器会复制/一份新的异常对象,即调用一次一份新的异常对象,即调用一次CMyException/类的拷贝构造函数。新拷贝的对象是个临时变量。类的拷贝构造函数。新拷贝的对象是个临时变量。throwobj1;catch(CMyExceptione)/当异常参数传递给当异常参数传递给e时,由于是传值方式,因时,由于是传值方式,因/此会调用一次拷贝构造函数此会调用一次拷贝构造函数cout“捕获一个捕获一个CMyException类型异常,类型异常,名称为:名称为:e.GetName()endl;cout程序运行结束!程序运行结束!endl;1/16/2023355.5.1传值方式传递异常对象传值方式传递异常对象程序运行的结果为:程序运行的结果为:构造一个构造一个CMyException对象,名称为:对象,名称为:obj1拷贝一个拷贝一个CMyException对象,名称为:对象,名称为:obj1拷贝一个拷贝一个CMyException对象,名称为:对象,名称为:obj1销毁一个销毁一个CMyException对象,名称为:对象,名称为:obj1捕获到一个捕获到一个CMyException类型的异常,名称为:类型的异常,名称为:obj1销毁一个销毁一个CMyException对象,名称为:对象,名称为:obj1销毁一个销毁一个CMyException对象,名称为:对象,名称为:obj1程序运行结束!程序运行结束!1/16/2023365.5.1传值方式传递异常对象传值方式传递异常对象n例例5.10中,调用了中,调用了1次次CMyException类的构造类的构造函数,函数,2次次CMyException类的拷贝构造函数。类的拷贝构造函数。n在在main()的的try块中,定义对象块中,定义对象obj1时,调用时,调用1次次CMyException类的构造函数。类的构造函数。n通过通过throw语句抛出异常对象语句抛出异常对象obj1时,复制了时,复制了一份一份obj1的临时局部变量,调用的临时局部变量,调用1次次CMyException类的拷贝构造函数类的拷贝构造函数n找到匹配的找到匹配的catch块后,由于是按值传递异常参块后,由于是按值传递异常参数,又调用一次数,又调用一次CMyException类的拷贝构造函类的拷贝构造函数。数。1/16/2023375.5.1传值方式传递异常对象传值方式传递异常对象n例例5.10中,调用了中,调用了3次次CMyException类的析类的析构函数。构函数。n在进入在进入catch块之后,进行异常处理之前,调用块之后,进行异常处理之前,调用了一次了一次CMyException类的析构函数,这时析类的析构函数,这时析构的是构的是try块中定义的局部变量块中定义的局部变量obj1。n在离开在离开catch块时,又调用了两次块时,又调用了两次CMyException类的析构函数,先析构的是进类的析构函数,先析构的是进入入catch块是构造的异常参数块是构造的异常参数obj1,然后再析,然后再析构构throw语句创建的临时局部变量语句创建的临时局部变量obj1。1/16/2023385.5.2引用方式传递异常对象引用方式传递异常对象按引用方式传递异常对象时,被抛出的异常也按引用方式传递异常对象时,被抛出的异常也是临时局部变量。是临时局部变量。【例【例5.11】按引用方式传递异常对象的例题。】按引用方式传递异常对象的例题。#include#includeusingnamespacestd;/插入例插入例5.10中中CMyException类的定义类的定义voidmain()try/构造一个异常对象,这是个局部变量。构造一个异常对象,这是个局部变量。CMyExceptionobj1(obj1);1/16/202339/这里抛出异常对象。注意:这时这里抛出异常对象。注意:这时VC编译器会复制编译器会复制/一份新的异常对象,即调用一次一份新的异常对象,即调用一次CMyException/类的拷贝构造函数。新拷贝的对象是个临时变量。类的拷贝构造函数。新拷贝的对象是个临时变量。throwobj1;catch(CMyException&e)/按引用方式传值按引用方式传值/此处传递给此处传递给e的实际是上面临时对象的引用,的实际是上面临时对象的引用,/因此不调用任何构造函数。因此不调用任何构造函数。cout“捕获一个捕获一个CMyException类型异常,类型异常,名称为:名称为:e.GetName()endl;cout程序运行结束!程序运行结束!endl;1/16/2023405.5.2引用方式传递异常对象引用方式传递异常对象程序运行的结果为:程序运行的结果为:构造一个构造一个CMyException异常对象,名称为:异常对象,名称为:obj1拷贝一个拷贝一个CMyException异常对象,名称为:异常对象,名称为:obj1销毁一个销毁一个CMyException异常对象,名称为:异常对象,名称为:obj1捕获到一个捕获到一个CMyException类型的异常,名称为:类型的异常,名称为:obj1销毁一个销毁一个CMyException异常对象,名称为:异常对象,名称为:obj11/16/2023415.5.2引用方式传递异常对象引用方式传递异常对象n例例5.11中,调用了中,调用了2次次CMyException类的构类的构造函数。造函数。n在在main()的的try块中,定义对象块中,定义对象obj1时,调用时,调用1次次CMyException类的构造函数。类的构造函数。n通过通过throw语句抛出异常对象语句抛出异常对象obj1时,复制了时,复制了一份一份obj1的临时局部变量,调用的临时局部变量,调用1次次CMyException类的拷贝构造函数类的拷贝构造函数n由于由于catch块是按引用方式传递异常对象,传块是按引用方式传递异常对象,传递给递给catch块的是临时异常对象的引用,因而不块的是临时异常对象的引用,因而不需要调用异常类的构造函数。需要调用异常类的构造函数。1/16/2023425.5.2引用方式传递异常对象引用方式传递异常对象n在例在例5.11中,调用了中,调用了2次次CMyException类的类的析构函数。析构函数。n在进入在进入catch块之后,进行异常处理之前,调块之后,进行异常处理之前,调用了一次用了一次CMyException类的析构函数,这时类的析构函数,这时析构的是析构的是try块中定义的局部变量块中定义的局部变量obj1。n在退出在退出catch块时,又调用了一次块时,又调用了一次CMyException类的析构函数,这时析构的是类的析构函数,这时析构的是调用调用throw语句时构建的临时异常对象。语句时构建的临时异常对象。1/16/2023435.5.3指针方式传递异常对象指针方式传递异常对象与传值方式和引用方式传递异常对象相比,在与传值方式和引用方式传递异常对象相比,在按指针方式传递异常时,异常对象的构造方式按指针方式传递异常时,异常对象的构造方式有很大的不同。它要么是在堆中动态构造的异有很大的不同。它要么是在堆中动态构造的异常对象,要么是静态全局对象,而不能是局部常对象,要么是静态全局对象,而不能是局部变量。变量。【例【例5.12】按引用方式传递异常的例题。】按引用方式传递异常的例题。#include#include using namespace std;/插入例插入例5.10中中CMyException类的定义类的定义 void main()1/16/202344try/动态在堆中构造的异常对象,调用一次构造函数。动态在堆中构造的异常对象,调用一次构造函数。throw new CMyException(obj1);/注意:这里是定义了按指针方式传递异常对象注意:这里是定义了按指针方式传递异常对象catch(CMyException*e)/此处传递给此处传递给e的实际是上面动态对象的地址,的实际是上面动态对象的地址,/因此不调用任何构造函数。因此不调用任何构造函数。cout“捕获到一个捕获到一个CMyException*类型的异类型的异常,名称为:常,名称为:GetName()endl;deletee;/动态创建的对象需要销毁动态创建的对象需要销毁程序运行的结果为:程序运行的结果为:构造一个构造一个CMyException异常对象,名称为:异常对象,名称为:obj1捕获到一个捕获到一个CMyException*类型的异常,名称为:类型的异常,名称为:obj1销毁一个销毁一个CMyException异常对象,名称为:异常对象,名称为:obj11/16/2023455.5.3指针方式传递异常对象指针方式传递异常对象n例例5.12中,只在异常抛出时调用了一次中,只在异常抛出时调用了一次CMyException类的构造函数。类的构造函数。n动态地创建对象,也要动态的销毁,因此,在动态地创建对象,也要动态的销毁,因此,在退出退出catch块之前调用块之前调用delete语句销毁异常对语句销毁异常对象时调用了一次象时调用了一次CMyException类的析构函类的析构函数。数。1/16/2023465.5.4异常对象传递方式的比较异常对象传递方式的比较传递方式传递方式传值方式传值方式引用方式引用方式指针方式指针方式抛出对象抛出对象局部临时对局部临时对象象局部临时对象局部临时对象堆对象堆对象全局静态对象全局静态对象catch语句语句catch(类型类型 e)catch(类型类型&e)catch(类型类型*e)构造对象构造对象次数次数321运行效率运行效率低低中中高高1/16/2023475.5.4异常对象传递方式的比较异常对象传递方式的比较传递方式传递方式传值方式传值方式引用方式引用方式指针方式指针方式异常对异常对象销毁象销毁局部变量离开作用域局部变量离开作用域时销毁;时销毁;临时变量临时变量catch块执行完后销毁;块执行完后销毁;异常类型参数在异常类型参数在catch块执行完后销毁。块执行完后销毁。局部变量离开局部变量离开作用域时销毁;作用域时销毁;临时变量在临时变量在catch 块执行完后块执行完后销毁。销毁。在在catch块最块最后显式调用后显式调用delete语句语句销毁。销毁。安全性安全性中中高高低,依赖程低,依赖程序员的能力序员的能力综合性能综合性能差差好好中中易使用性易使用性好好好好一般一般1/16/2023485.6标准标准C+库中的异常类库中的异常类n在标准在标准C+库中提供了一个异常类的基类库中提供了一个异常类的基类exception和它的多个派生类。分别介绍如下。和它的多个派生类。分别介绍如下。n1.头文件中定义了异常类头文件中定义了异常类exception和和bad_exception,异常类,异常类exception是标准是标准C+库中库中所有异常类的基类。所有异常类的基类。n2.头文件中定义了异常类头文件中定义了异常类ios_base:failure。n3.头文件中定义了异常类头文件中定义了异常类bad_cast和和bad_typeid,当,当dynamic_cast失败时将抛出该异常失败时将抛出该异常类对象。类对象。n4.头文件中定义了其它所有的异常类,头文件中定义了其它所有的异常类,如如logic_error、out_of_range、range_error、runtime_error等。等。1/16/202349range_error overflow_error exception bad_typeid runtime_errorbad_cast ios_base:failure logic_error bad_exception bad_alloc invalid_argument out_of_range domain_error leng_error underflow_error C+标准异常类层次结构标准异常类层次结构1/16/2023505.7综合应用实例综合应用实例异常处理是在实际开发中为了避免程序出现不正常的运异常处理是在实际开发中为了避免程序出现不正常的运行情况而采用的一种机制,它要和实际开发结合起来行情况而采用的一种机制,它要和实际开发结合起来才有实际意义。然而,为了更好的了解才有实际意义。然而,为了更好的了解C+实际处理实际处理异常的方式,下面的例子中避开了实际的应用。仅根异常的方式,下面的例子中避开了实际的应用。仅根据程序输入的不同,进行不同的异常处理,以更好的据程序输入的不同,进行不同的异常处理,以更好的理解理解C+的异常处理。的异常处理。【实例一】【实例一】C+的异常处理机制例题。的异常处理机制例题。n#includen#includen/此处包含例此处包含例5.10中的中的CMyException类代码类代码1/16/202351nclassCTestClassn/测试类,其构造函数可能抛出测试类,其构造函数可能抛出int型或型或char*型异常型异常npublic:nCTestClass(intx)throw(int);nvoidprint();nprivate:ninta;n;nCTestClass:CTestClass(intx)throw(int)n/可能抛出可能抛出int型或型或char*型异常,但其本身不处理型异常,但其本身不处理int类型异常类型异常ntrynif(x=0)nthrow0;nif(x1000)nthrowx值太大!值太大!;1/16/202352na=100/x;nncatch(char*s)nncout处理了处理了char*类型异常信息:类型异常信息:sendl;nnnvoidCTestClass:print()nncoutaendl;nnvoidfunc8(intx)throw(CMyException,int)n/可能抛出可能抛出CMyException,int类型异常类型异常nCTestClassa(x);na.print();nCMyExceptionobj2(obj2);1/16/202353nthrowobj2;nnvoidfunc9()throw(char*)n/可能抛出可能抛出char*类型异常类型异常nchar*p=newchar20;ntry/可能引起异常的代码可能引起异常的代码nthrowerror;nncatch(.)n/释放申请的空间释放申请的空间p后将异常继续抛出后将异常继续抛出ncout释放申请的空间释放申请的空间endl;ndeletep;nthrow;nn1/16/202354nvoidmain()nntry/可能产生异常的代码可能产生异常的代码nthrownewCMyException(obj1);nncatch(CMyException*e)nncout“捕获一个捕获一个CMyException类型的异常,名称为:类型的异常,名称为:nGetName()endl;ndeletee;nntryncoutx;nfunc8(x);nfunc9();n1/16/202355ncatch(intx)nncout处理了处理了int类型的异常:类