C程序设计第5章_第1页
C程序设计第5章_第2页
C程序设计第5章_第3页
C程序设计第5章_第4页
C程序设计第5章_第5页
已阅读5页,还剩51页未读 继续免费阅读

下载本文档

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

文档简介

会计学1C程序设计第5章2023/1/1725.1异常的概念

5.1.1异常的概念程序运行过程中,由于环境变化、用户操作失误以及其它方面的原因而产生的运行时不正常的情况,它要求程序立即进行处理,否则将会引起程序错误甚至崩溃的现象。常见的异常有:空闲内存耗尽、请求打开不存在的文件、被0除、打印机未打开、数组越界访问等。第1页/共56页2023/1/1735.1.2异常的产生C++程序是由一些相互分离的模块组成的,程序中出现错误和解决出现的错误就会分成两个部分:(1)某个模块A出现错误,但它并没有能力在模块A内解决这个错误,因此它就给出关于这个错误的报告。(2)某个模块B能够检测到模块A发出的错误报告,并处理这个错误,使出现错误造成的损失减到最小。第2页/共56页2023/1/1745.2异常处理机制5.2.1基本概念1.抛出异常如果程序发生异常情况,而在当前的上下文环境中获取不到处理这个异常的足够信息,程序将创建一个包含出错信息的对象并将该对象抛出当前上下文环境,将错误信息发送到更大的上下文环境中,这个过程称为抛出(throw)异常。第3页/共56页2023/1/1755.2.1基本概念2.捕捉异常对于一个抛出的异常,如果某一个模块能够(或想要)处理这个异常,它就可以获得程序的控制权处理该异常,这个过程称为捕捉(catch)异常。

3.处理异常当某个catch块捕捉到异常后,它就根据事先制定的策略对异常进行处理,这就是处理异常。在C++中,只有catch块能够捕获异常并进行处理,因此catch块又称为异常处理器。第4页/共56页2023/1/1765.2.1基本概念4.C++的异常处理机制

C++的异常处理机制就是将抛出异常与捕捉异常、处理异常分离开来。抛出异常的模块并不负责异常的处理,它只是报告某个地方存在错误,这个报告可以帮助异常处理器解决这个错误。而异常处理器则根据抛出异常模块的报告来处理异常,如果没有模块抛出异常,就不会有异常的处理。第5页/共56页2023/1/1775.2.2throw语句抛出异常的语法格式如下:throw表达式这里,throw后的表达式表示异常的类型,它可以是一个变量或一个对象。throw语句在语法上与return语句相似。下面是两条throw语句的例子。throw1;throw(“出现异常”);异常抛出后,程序的控制权就从异常抛出的地方交出,由编译器寻找匹配的异常处理器进行相应的处理。

第6页/共56页2023/1/1785.2.3try块try块的语法格式如下:

try{

复合语句

}try块必须包围能够抛出异常的语句。它提示编译器到那里查找异常处理器,没有跟在try块后的catch块是没有用的。

try块可以包含任何C++语句,甚至包含整个函数。第7页/共56页2023/1/1795.2.4catch块catch块的语法格式如下。catch(异常类型声明){异常处理语句}catch(异常类型声明){异常处理语句}……第8页/共56页2023/1/17105.2.4catch块catch后括号中的异常类型声明可以是一个类型或一个对象声明,后边一对“{}”括住的是一组复合语句。一个catch块相当于一个以类型为单一参数的函数。catch块必须直接放在try块之后。catch语句与switch语句不同,它不需要在每个case语句后加入break用以中断后面程序的执行。一个catch块引入一个局部域,在catch块内声明的变量不能在catch块外引用。第9页/共56页2023/1/1711【例5.2】局部域声明的变量不能被局部域外引用例题。#include<iostream.h>voidmain(){try{//一段可能引起异常的代码

throw("出现异常!");}catch(char*message){inty=1;cout<<"处理了char*类型的异常"<<endl;}cout<<y<<endl;//编译错误,变量y未定义}第10页/共56页2023/1/17125.2.5异常处理模式C++的异常处理有两种基本模式:

1.终止模式异常抛出后,捕捉异常并退出导致异常的子程序或子系统,退出需要关闭适当的文件,析构适当的对象,释放适当的内存,处理需要处理的设备等,这种方法称为终止模式。缺省情况下,C++异常处理机制采用终止方法。

【例5.3】捕捉异常后直接退出的例题。第11页/共56页2023/1/1713#include<iostream.h>voidfunc2(){ //一段有可能引起异常的代码

throw1; cout<<"其它程序语句!"<<endl;}voidmain(){ try{

第12页/共56页2023/1/1714func2();//func2()抛出的异常值为1 //程序抛出异常后的语句部分

cout<<"异常处理结束后继续执行!"<<endl; }catch(intx){ //对异常的处理

cout<<"处理了int类型的异常"<<endl;}cout<<"程序结束!"<<endl;}在采用终止模式情况下程序的运行结果为:处理了int类型的异常!程序结束!

第13页/共56页2023/1/17155.2.5异常处理模式

2.恢复模式异常抛出后,捕捉异常并试图去纠正或调整引起异常的条件,然后从发生异常的地方继续执行,这种方法称为恢复模式。

恢复模式实现起来非常困难,在实际应用中,除了一些特殊的领域外,一般都不采用恢复模式处理异常。第14页/共56页2023/1/17165.2.6重新抛出在异常处理过程中也可能存在“单个catch子句不能完全处理这个异常”的情况。那么该异常处理器在做完局部能够做的事情后,会再一次抛出这个异常,让函数调用链中更上级的函数来处理,这个过程称作重新抛出(rethrow)重新抛出的语法形式如下:

throw;重新抛出的还是原来捕捉到的那个异常。重新抛出只能出现在catch块中。第15页/共56页2023/1/1717【例5.4】重新抛出捕捉的异常例题。#include<iostream.h>voidfunc3(intx){ try {//一段有可能引起异常的代码

throwx; } catch(intx) {//如果异常参数x<=0则进行处理,否则继续抛出

if(x<=0) {//对异常的处理}else {cout<<"重新抛出异常!"<<endl; 第16页/共56页2023/1/1718 throw;//重新抛出

} }}voidmain(){ try {func3(1);//程序其它部分 } catch(intx) { //对异常的处理

cout<<"处理了int类型的异常!"<<endl; }}第17页/共56页2023/1/1719程序运行结果为:

重新抛出异常!处理了int类型的异常!有的情况下,异常处理器在重新抛出之前会对异常信号进行一些修改,这个修改能够影响更高级函数调用链中的异常处理器对该异常的处理。5.2.6重新抛出第18页/共56页2023/1/17205.2.7异常规范异常规范规定:随着函数声明列出该函数可能抛出的异常,并保证该函数不会抛出其它类型的异常。常见附带异常说明的函数说明有以下3种情况。(1)函数返回类型函数名(参数列表)throw(类型列表);(2)函数返回类型函数名(参数列表)throw();(3)函数返回类型函数名(参数列表);第一种情况,函数列出所有可能抛出的异常类型;第二种情况表示函数不会抛出任何类型的异常;第三种情况表示函数可能抛出任何类型的异常。异常规范并非强制规定,因此,没有在函数说明后附带异常说明并非语法错误。第19页/共56页2023/1/1721【例5.6】异常规范的处理例题voidfunc5(intx)throw(int,char*){//x等于0抛出int型异常,x小于0抛出char*型异常,x大于0//什么也不做

if(x==0) throw0; if(x<0) throw"error";}voidfunc6()throw(){//本函数完成10个指令周期的延时,不抛出任何异常

inti=0; while(i<10) i++;}第20页/共56页2023/1/17225.2.7异常规范

有时,函数可能抛出没有列入异常规范的异常,出现这种情况时,系统分两种情况进行处理。(1)在函数内部(包括抛出异常的函数以及调用该函数的函数链中的任意函数)捕捉到了这个异常,进行了处理,则程序可以继续执行。。(2)异常被抛到函数外部,系统会调用C++标准库中定义的函数unexpected(),该函数的缺省行为是调用terminate(),终止程序的运行。当然,在C++中,可以改变unexpected()的缺省行为。第21页/共56页2023/1/17235.3没有被捕捉的异常根据异常匹配的规则,如果try块后面的所有的catch块都没有与某一异常相匹配,这时内层对异常的捕获失败,异常将进入更高层的上下文环境中进行匹配,这个过程一直进行直到在某个层次异常处理器与该异常相匹配,这时才认为捕获了这个异常。第22页/共56页2023/1/17245.3没有被捕捉的异常如果任意层的异常处理器都没有捕获到这个异常,那么这个异常最终会抛给main()函数,如果在main()中还没有找到合适的匹配,则称这个异常是“未捕捉的”或“未处理的”。如果一个异常未被捕捉,就会调用函数terminate(),终止本程序的运行。第23页/共56页2023/1/1725【例5.7】未被捕捉的异常处理例题

#include<iostream.h>voidfunc7(){throw0;}voidmain(){try{func7();}catch(doubled){cout<<"进行了异常处理!"<<endl;}}第24页/共56页2023/1/1726例子5.7中,func7()函数中抛出了int型的异常,最后抛给了main()函数,在main()函数中也没找到合适的匹配,于是终止本程序运行,main()函数catch块后边的程序其它部分不再执行。如果在所有函数之外的代码出现异常,比如全局对象的构造和析构等,如果有相应的异常处理器捕捉到抛出的异常,则异常处理后继续main()函数的执行,如果没有捕捉到抛出的异常,则终止本程序运行。5.3没有被捕捉的异常第25页/共56页2023/1/17275.4catch(...)使用C++在异常处理中提供了一个能捕捉所有异常的catch块。catch块的语法格式如下:catch(…){异常处理语句}其中,列表中的“…”表示可捕获所有的异常,但使用省略号就不可能有参数,也不可能知道所接受到的异常为何种类型。其它部分和普通catch块完全一样。第26页/共56页2023/1/1728【例5.9】使用catch(...)语句的异常处理例题#include<iostream.h>voidfunc5(intx)throw(int){//x等于0抛出int型异常,x小于0抛出char*型异常

if(x==0)throw0; if(x<0)throw"error";}voidmain(){ try {intx=0; cout<<"请输入一个int类型数据:"; cin>>x;第27页/共56页2023/1/1729func5(x); //程序其它部分

} catch(...) { //对异常的处理

cout<<"处理了所有类型的异常!"<<endl; } cout<<"程序结束!"<<endl;}程序运行结果如下:

请输入一个int类型数据:0

处理了所有类型的异常!第28页/共56页2023/1/17305.5用类的对象传递异常异常信息传递是指将throw语句抛出的异常参数传递到catch块中。但在实际应用中,由于抛出异常信息的需要,经常使用类的对象传递异常。使用对象传递异常还有以下2个好处:(1)在C++中,很好地实现了RTTI(Run-TimeTypeInformation)技术,使用对象传递异常,可以很好地完成异常对象的类型匹配。(2)在C++中,很好地实现了对象的构造、销毁、转存复制等技术,可以很好的实现异常信息的传递、修改和销毁等。同函数参数传递方式一样,异常参数的传递有3种方式:传值方式、引用方式和指针方式第29页/共56页2023/1/17315.5.1传值方式传递异常对象按传值方式传递异常对象时,被抛出的异常都是局部变量,而且是临时的局部变量。也就是说,每当在throw语句抛出一个异常对象时,不管构造的对象是什么性质的变量,此时它都会复制一份临时局部变量。

【例5.10】按传值方式传递异常对象例题。

#include<string>#include<iostream>usingnamespacestd;第30页/共56页2023/1/1732classCMyException{//异常类,该类的对象作为抛出异常时传递的异常参数。public:CMyException(stringn="none"):name(n){//根据参数n构造一个名字为n的异常类对象

cout<<“构造一个CMyException对象,名称为:"<<name<<endl;}CMyException(constCMyException&e){//根据参数e拷贝构造一个异常类对象

name=;cout<<“拷贝一个CMyException对象,名称为:"<<name<<endl;}第31页/共56页2023/1/1733virtual~CMyException(){cout<<“销毁一个CMyException对象,名称为:"<<name<<endl;}stringGetName(){returnname;}protected:stringname;//异常类对象的名字};voidmain(){try{//构造一个异常对象,这是个局部变量。

CMyExceptionobj1("obj1");第32页/共56页2023/1/1734//下面抛出异常对象。注意:这时VC编译器会复制

//一份新的异常对象,即调用一次CMyException//类的拷贝构造函数。新拷贝的对象是个临时变量。

throwobj1;}catch(CMyExceptione){//当异常参数传递给e时,由于是传值方式,因

//此会调用一次拷贝构造函数

cout<<“捕获一个CMyException类型异常,名称为:"<<e.GetName()<<endl;}cout<<"程序运行结束!"<<endl;}第33页/共56页2023/1/17355.5.1传值方式传递异常对象程序运行的结果为:构造一个CMyException对象,名称为:obj1拷贝一个CMyException对象,名称为:obj1拷贝一个CMyException对象,名称为:obj1销毁一个CMyException对象,名称为:obj1捕获到一个CMyException类型的异常,名称为:obj1销毁一个CMyException对象,名称为:obj1销毁一个CMyException对象,名称为:obj1程序运行结束!第34页/共56页2023/1/17365.5.1传值方式传递异常对象例5.10中,调用了1次CMyException类的构造函数,2次CMyException类的拷贝构造函数。在main()的try块中,定义对象obj1时,调用1次CMyException类的构造函数。通过throw语句抛出异常对象obj1时,复制了一份obj1的临时局部变量,调用1次CMyException类的拷贝构造函数找到匹配的catch块后,由于是按值传递异常参数,又调用一次CMyException类的拷贝构造函数。第35页/共56页2023/1/17375.5.1传值方式传递异常对象例5.10中,调用了3次CMyException类的析构函数。在进入catch块之后,进行异常处理之前,调用了一次CMyException类的析构函数,这时析构的是try块中定义的局部变量obj1。在离开catch块时,又调用了两次CMyException类的析构函数,先析构的是进入catch块是构造的异常参数obj1,然后再析构throw语句创建的临时局部变量obj1。第36页/共56页2023/1/17385.5.2引用方式传递异常对象

按引用方式传递异常对象时,被抛出的异常也是临时局部变量。【例5.11】按引用方式传递异常对象的例题。#include<string>#include<iostream>usingnamespacestd;//插入例5.10中CMyException类的定义voidmain(){try{//构造一个异常对象,这是个局部变量。

CMyExceptionobj1("obj1");第37页/共56页2023/1/1739//这里抛出异常对象。注意:这时VC编译器会复制

//一份新的异常对象,即调用一次CMyException//类的拷贝构造函数。新拷贝的对象是个临时变量。

throwobj1;}catch(CMyException&e)//按引用方式传值

{//此处传递给e的实际是上面临时对象的引用,

//因此不调用任何构造函数。

cout<<“捕获一个CMyException类型异常,名称为:"<<e.GetName()<<endl;}cout<<"程序运行结束!"<<endl;}第38页/共56页2023/1/17405.5.2引用方式传递异常对象程序运行的结果为:构造一个CMyException异常对象,名称为:obj1拷贝一个CMyException异常对象,名称为:obj1销毁一个CMyException异常对象,名称为:obj1捕获到一个CMyException类型的异常,名称为:obj1销毁一个CMyException异常对象,名称为:obj1第39页/共56页2023/1/17415.5.2引用方式传递异常对象例5.11中,调用了2次CMyException类的构造函数。在main()的try块中,定义对象obj1时,调用1次CMyException类的构造函数。通过throw语句抛出异常对象obj1时,复制了一份obj1的临时局部变量,调用1次CMyException类的拷贝构造函数由于catch块是按引用方式传递异常对象,传递给catch块的是临时异常对象的引用,因而不需要调用异常类的构造函数。第40页/共56页2023/1/17425.5.2引用方式传递异常对象在例5.11中,调用了2次CMyException类的析构函数。在进入catch块之后,进行异常处理之前,调用了一次CMyException类的析构函数,这时析构的是try块中定义的局部变量obj1。在退出catch块时,又调用了一次CMyException类的析构函数,这时析构的是调用throw语句时构建的临时异常对象。第41页/共56页2023/1/17435.5.3指针方式传递异常对象

与传值方式和引用方式传递异常对象相比,在按指针方式传递异常时,异常对象的构造方式有很大的不同。它要么是在堆中动态构造的异常对象,要么是静态全局对象,而不能是局部变量。

【例5.12】按引用方式传递异常的例题。

#include<string>#include<iostream>usingnamespacestd;//插入例5.10中CMyException类的定义

voidmain(){第42页/共56页2023/1/1744

try{//动态在堆中构造的异常对象,调用一次构造函数。

thrownewCMyException("obj1");}//注意:这里是定义了按指针方式传递异常对象

catch(CMyException*e){//此处传递给e的实际是上面动态对象的地址,

//因此不调用任何构造函数。

cout<<“捕获到一个CMyException*类型的异常,名称为:"<<e->GetName()<<endl;deletee;//动态创建的对象需要销毁

}}程序运行的结果为:构造一个CMyException异常对象,名称为:obj1捕获到一个CMyException*类型的异常,名称为:obj1销毁一个CMyException异常对象,名称为:obj1第43页/共56页2023/1/17455.5.3指针方式传递异常对象例5.12中,只在异常抛出时调用了一次CMyException类的构造函数。动态地创建对象,也要动态的销毁,因此,在退出catch块之前调用delete语句销毁异常对象时调用了一次CMyException类的析构函数。第44页/共56页2023/1/17465.5.4异常对象传递方式的比较传递方式传值方式引用方式指针方式抛出对象局部临时对象局部临时对象堆对象全局静态对象catch语句catch(类型e)catch(类型&e)catch(类型*e)构造对象次数321运行效率低中高第45页/共56页2023/1/17475.5.4异常对象传递方式的比较传递方式传值方式引用方式指针方式异常对象销毁①局部变量离开作用域时销毁;②临时变量catch块执行完后销毁;③异常类型参数在catch块执行完后销毁。①局部变量离开作用域时销毁;②临时变量在catch块执行完后销毁。在catch块最后显式调用delete语句销毁。安全性中高低,依赖程序员的能力综合性能差好中易使用性好好一般第46页/共56页2023/1/17485.6标准C++库中的异常类在标准C++库中提供了一个异常类的基类exception和它的多个派生类。分别介绍如下。1.<exception>头文件中定义了异常类exception和bad_exception,异常类exception是标准C++库中所有异常类的基类。2.<ios>头文件中定义了异常类ios_base::failure。3.<type_info>头文件中定义了异常类bad_cast和bad_typeid,当dynamic_cast失败时将抛出该异常类对象。4.<stdexcept>头文件中定义了其它所有的异常类,如logic_error

、out_of_range

、range_error、runtime_error等。第47页/共56页2023/1/1749range_error

overflow_error

exception

bad_typeid

runtime_error

bad_cast

ios_base::failure

logic_error

bad_exception

bad_alloc

invalid_argument

out_of_range

domain_error

leng_error

underflow_error

C++标准异常类层次结构第48页/共56页2023/1/17505.7综合应用实例

异常处理是在实际开发中为了避免程序出现不正常的运行情况而采用的一种机制,它要和实际开发结合起来才有实际意义。然而,为了更好的了解C++实际处理异常的方式,下面的例子中避开了实际的应用。仅根据程序输入的不同,进行不同的异常处理,以更好的理解C++的异常处理。【实例一】C++的异常处理机制例题。#include<string.h>#include<iostream.h>//此处包含例5.10中的CMyException类代码第49页/共56页2023/1/1751classCTestClass{//测试类,其构造函数可能抛出int型或char*型异常public:CTestClass(intx)throw(int);voidprint();private:inta;};CTestClass::CTestClass(intx)throw(int){//可能抛出int型或char*型异常,但其本身不处理int类型异常

try{if(x==0)throw0;if(x>1000)throw"x值太大!";第50页/共56页2023/1/1752a=100/x;}catch(char*s){cout<<"处理了char*类型异常信息:"

温馨提示

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

评论

0/150

提交评论