010异常处理.docx

上传人:b****7 文档编号:8808360 上传时间:2023-02-01 格式:DOCX 页数:32 大小:35.19KB
下载 相关 举报
010异常处理.docx_第1页
第1页 / 共32页
010异常处理.docx_第2页
第2页 / 共32页
010异常处理.docx_第3页
第3页 / 共32页
010异常处理.docx_第4页
第4页 / 共32页
010异常处理.docx_第5页
第5页 / 共32页
点击查看更多>>
下载资源
资源描述

010异常处理.docx

《010异常处理.docx》由会员分享,可在线阅读,更多相关《010异常处理.docx(32页珍藏版)》请在冰豆网上搜索。

010异常处理.docx

010异常处理

异常处理

异常处理1

1.异常处理一般性概念1

2.C++标准异常处理4

2.1.C++异常处理4

2.2.多个异常的组织6

2.3.异常接口说明10

3.MFC异常处理11

4.Win32结构化异常处理(SEH)13

4.1.__try{}__finally{}结构14

4.2.__try{}__except(){}结构15

1.异常处理一般性概念

修复技术是提高代码健壮性的最有效方法之一。

C语言中实现错误处理的方法是将函数调用与错误处理程序紧密结合起来,这使得错误处理的使用很不方便。

在传统的C语言程序设计中,一个函数的出错信息往往是通过函数的返回值获得,这就要求函数的使用者在每次的函数调用时都要进行相应的错误判断,有时,返回值所包含的错误信息较为复杂时,对出错信息的判断和处理也变的非常复杂,而且对同一函数的重复调用往往要伴随着对错误信息的重复判断和处理。

这样的异常处理方式使得程序代码既累赘,可读性又差。

而且往往因为缺乏条理而遗漏对某个错误的处理。

异常处理通过把异常的检查和异常的处理分开,使得程序的代码简洁、清晰、可读性强。

例:

假设有一个初始化数据库的函数InitDb(),它需要调用创建数据库的函数CreateDb(),创建用户及授权函数CreateUser(),创建表函数CreateTable(),CreateTable()又调用创建索引的函数CreateIndex():

每个函数都以返回值表示处理的结果,在InitDb()的返回值中,如果既要包含最终的错误信息,又要包含那一层次调用出错的信息,一种代码的形式为:

intCreateUser()

{

//...

}

intCreateIndex()

{

//...

}

intCreateTable()

{

//....

if((iRet=CreateIndex())<0)

//错误处理

}

intCreateDb()

{

//...

}

intInitDb()

{

if((iRet=CreateDb())==0)

{

if((iRet=CreateUser())==0)

{

if((iRet=CreateTable()==0))

{

//...

return0;

}

else

{

//错误处理

return-1;

}

}

else

{

//错误处理

return-1;

}

}

else

{

//错误处理

return?

?

}

}

//或者

intInitDb()

{

if((iRet=CreateDb())<0)

{

//错误处理

//return?

?

}

if((iRet=CreateUser())<0)

{

//错误处理

//return?

?

}

if((iRet=CreateTable()<0))

{

//错误处理

//return?

?

}

return0;

}

代码变得复杂而难以维护,而如果使用异常处理,可简化为如下形式

voidCallFunc()

{

try

{

InitDb();

}

catch(?

?

?

{

//异常处理

}

}

voidInitDb()

{

CreateDb();//在CreateDb()内部如果执行失败,抛出异常

CreateUser();//在CreateUser()内部如果执行失败,抛出异常

CreateTable();//在CreateTable()函数内部如果执行失败,抛出异常

}

2.C++标准异常处理

2.1.C++异常处理

C++的异常处理分为两部分,第一部分是检查程序处理情况,如果处理结果非期望的结果,则生成一个异常,并将异常抛出,抛出后,由异常的接收者处理这个异常,异常抛出的语法形式为:

throw异常对象

throw是C++关键字,表示要抛出异常。

异常对象表示异常信息,它既可以是普通数据类型的实例,也可以是类类型的对象实例。

异常处理的第二部分捕捉被抛出的异常,并对异常进行处理,其形式为:

try

{

//程序执行语句,可能抛出异常。

}

catch(异常类型[,参数])

{

//处理异常

}

其中try是关键字,其后的语句块叫try块,其中的一些语句直接(比如throw表达式)或者间接(比如对包含throw表达式的函数的调用)地抛出异常。

抛出的异常只有在try语句块中才能被catch语句捕获。

紧接着try块的是catch语句,用来捕获异常,catch之后的括号中含有数据类型参数,参数类型说明了希望捕获的异常的具体类型,catch中的参数类型之后的参数是可选的。

如果有参数,则异常被捕获时,抛出的异常对象会初始化参数。

catch中包含参数使得在被捕获的对象信息可以在异常处理程序中被使用。

catch后的语句块就是对应于被捕获的异常的处理程序。

例:

#include"stdafx.h"

#include

usingnamespacestd;

classCMyError

{

public:

CMyError()

{

cout<<"CMyError()"<

}

CMyError(constCMyError&obj)

{

cout<<"CMyError(constCMyError&obj)"<

}

~CMyError()

{

cout<<"~CMyError()"<

}

};

classCTest

{

public:

intFunc(inti)

{

if(i<10)

throwCMyError();//强制类型转换,抛出类类型实例

if(i<20)

throw-1;//抛出基本类型数据的实例

returni;

}

};

intmain(intargc,char*argv[])

{

CTesttest;

intiIn;

cin>>iIn;

try

{

intx=test.Func(iIn);

cout<<"Attheendoftryblock"<

}

//也可以是

//catch(CMyError)

//catch(CMyError&e)

//如果抛出来的是堆中分配的对象的指针

//此处也可以接收指针。

catch(CMyErrore)

{

cout<<"catchCErrorobject"<

intb=2;

}

//也可以接收标准数据类型的实例

catch(inti)

{

cout<<"catchintvariable:

"<

}

return0;

}

如果throw表达式或者抛出异常的子函数没在当前函数的try语句块中,或者虽然包含在try语句块中,但是catch捕捉的类型与实际抛出的类型不匹配,那系统将把异常抛给上一层次的函数,直到遇到一个能够处理改异常的函数。

例:

voidFunc1()

{

CTestt;

t.Func(3);

}

voidFunc2()

{

Func1();

}

voidFunc3()

{

try

{

Func2();

}

catch(...)

{

//...

}

}

如果直到最外层的main函数都没能捕捉到异常,则系统会自动调用函数:

voidterminate();

该函数在eh.h中定义,缺省情况下,terminate()将调用abort()来终止程序,abort()终止程序的同时输出缺省的异常提示信息。

//!

!

!

有问题!

!

!

程序员也可以通过函数set_terminate()改变最终的默认异常接收函数terminate,set_terminate()的原型为:

//typedefvoid(*pfv)();

//pfvset_terminate(pfv);

//但是不管你设置的异常接受函数内部怎么处理,函数执行完毕后,程序结束执行。

2.2.多个异常的组织

2.2.1.多个异常

●一个函数可以抛出多个异常,可以在try块后放置多个catch块来捕获多个异常,如上例;

●异常也可以嵌套,里层抛出的异常一层层地往外匹配catch块,直到匹配成功或者跳出main()交系统处理。

例:

intFunc()

{

try

{

try

{

throwchar

(1);

}

catch(intie)

{

cout<<"level1catch"<

}

}

catch(charce)

{

cout<<"level2catch"<

}

return0;

}

里层的异常将被外层的catch捕获。

●异常被捕获后,仍可将异常再次抛出以期待被外层的catch捕获。

再次抛出异常用关键字throw即可,不必加上再次抛出的异常名,实际上,它隐含地认为抛出的是当前捕获的异常对象。

例:

voidFunc2()

{

try

{

inti=9;

throwi;

}

catch(intie)

{

cout<<"Func2()catch"<

throw;

}

}

voidFunc()

{

try

{

Func2();

}

catch(intie)

{

cout<<"Func()catch"<

}

}

intmain(intargc,char*argv[])

{

Func();

return0;

}

2.2.2.用枚举组织异常

一个函数往往会产生各种异常,这就要求对各种异常进行组织和管理。

方法之一是用枚举来组织异常。

例:

enumENUM_ERROR{StackOverflow,NullPointer,ZeroDevide};

voidFunc(inti)

{

if(i%3==0)

throwStackOverflow;

if(i%4==0)

throwNullPointer;

if(i%5==0)

throwZeroDevide;

throw0;//注意,不同于throwENUM_ERROR(0);

}

intmain(intargc,char*argv[])

{

inti;

cin>>i;

try

{

Func(i);

}

catch(ENUM_ERRORe)

{

switch(e)

{

caseStackOverflow:

cout<<"StackOverflow"<

break;

caseNullPointer:

cout<<"NullPointer"<

break;

caseZeroDevide:

cout<<"ZeroDevide"<

break;

}

}

catch(...)

{

cout<<"Unknownexception!

"<

}

return0;

}

2.2.3.用虚函数组织异常

利用虚函数组织异常可以使得在增加新的异常时不必修改源程序,同时又能按具体的异常对象自动选择合适的异常处理程序。

只要把异常类中与异常处理有关的函数声明为虚函数,并把catch()中的类型参数设定为基类的引用或者指针就行了。

这样,在异常处理中只要统一按照对基类异常的处理就行了。

例:

classCErrorBase

{

public:

virtualvoidPrintError()

{

cout<<"CErrorBase"<

}

};

classCErrorOverflow:

publicCErrorBase

{

public:

virtualvoidPrintError()

{

cout<<"CErrorOverflow"<

}

};

classCErrorNullPointer:

publicCErrorBase

{

public:

virtualvoidPrintError()

{

cout<<"CErrorNullPointer"<

}

};

classCErrorZeroDevide:

publicCErrorBase

{

public:

virtualvoidPrintError()

{

cout<<"CErrorZeroDevide"<

}

};

voidFunc(inti)

{

if(i%3==0)

throwCErrorOverflow();

if(i%4==0)

throwCErrorNullPointer();

if(i%5==0)

throwCErrorZeroDevide();

}

intmain(intargc,char*argv[])

{

inti;

cin>>i;

try

{

Func(i);

}

catch(CErrorBase&e)

{

e.PrintError();

}

return0;

}

这样使得对异常的处理变得简单而统一,事实上,MFC的异常处理就是以此为基础的。

2.3.异常接口说明

一个函数往往要抛出异常对象,函数的使用者应当了解函数所抛出的异常,从而设置catch语句捕获异常对象,并对捕获的异常对象进行处理,但是,除非看到函数的源代码,否则函数的使用者无法从函数的原型声明中知道函数可能抛出的异常。

C++为此提供了异常的接口说明,它在函数说明中就列出了函数可能抛出的异常,因为不必了解函数代码就可以知道函数与哪些异常有关。

异常接口说明是函数声明的一部分,它紧跟在普通的函数声明之后。

异常接口说明的一般形式为:

返回类型函数名(参数列表)throw(异常类型1,异常类型2,,,);

throw后扩后内的内容是此函数可能抛出的所有异常的集合。

各类型间用逗号隔开。

例如:

voidfunc(inti)throw(char,int);

说明函数func只可能抛出char类型和int类新的异常对象,而不会再抛出其他异常。

如果一个函数声明中没有异常说明列表,那么隐含地表示函数可以抛出任何类型的异常。

例如:

intFunc(inti);

表示Func函数可能会抛出各种类型的异常。

如果规定函数不会抛出任何异常,则应该设置函数声明的异常接口为空,例如:

intFunc(inti)throw();

说明Func函数不会抛出任何异常。

 

3.MFC异常处理

MFC异常的语法和语义是构建在标准C++异常的语法和语义的基础上的,是用宏的方式对标准C++的异常进行包装。

MFC异常类体系是以虚函数方式组织的。

CException是所有MFC异常类的基类,

所有名字为CXXXException形式的类都是从抽象类CException派生的。

下表显示由MFC提供的预定义异常。

异常类

含义

CMemoryException

内存不足

CFileException

文件异常

CArchiveException

存档/序列化异常

CNotSupportedException

响应对不支持服务的请求

CResourceExceptionWindows

资源分配异常

CDaoException

数据库异常(DAO类)

CDBException

数据库异常(ODBC类)

COleException

OLE异常

COleDispatchException

调度(自动化)异常

CUserException

用消息框警告用户然后引发一般CException的异常

MFC则定义了一组宏:

TRY

CATCH,AND_CATCH,和END_CATCH

THROW和THROW_LAST

这些宏非常象C++的异常关键字try、catch和throw。

对于每个MFC异常类CXXXException,都有一个全局的辅助函数AfxThrowXXXException(),它构造、初始化和抛出这个类的对象。

你可以用这些辅助函数处理预定义的异常类型,用THROW处理自定义的对象(当然,它们必须是从CException派生的)。

基本的设计原则是:

用TRY块包含可能产生异常的代码。

用CATCH检测并处理异常。

异常处理函数并不是真的捕获对象,它们其实是捕获了指向异常对象的指针。

MFC靠动态类型来辨别异常对象。

可以在一个TRY块上捆绑多个异常处理函数,每个捕获一个C++静态类型的不同的对象。

第一个处理函数使用宏CATCH,以后的使用AND_CATCH,用END_CATCH结束处理函数队列。

MFC自己可能触发异常,你也可以显式触发异常(通过THROW或MFC辅助函数)。

在异常处理函数内部,可以用THROW_LAST再次抛出最近一次捕获的异常。

例,使用MFC内设异常类:

voidFunc2()

{

TRY

{

printf("raisingmemoryexception\n");

AfxThrowMemoryException();

printf("thislineshouldneverappear\n");

}

CATCH(CException,pe)//!

注意,抛出的是对象的指针

{

printf("caughtgenericexception;rethrowing\n");

THROW_LAST();//重新抛出必须用THROW_LAST();而不是THROW()

printf("thislineshouldneverappear\n");

}

END_CATCH

printf("thislineshouldneverappear\n");

}

voidFunc3()

{

TRY

{

Func2();

printf("thislineshouldneverappear\n");

}

CATCH(CFileException,pe)

{

printf("caughtfileexception\n");

}

AND_CATCH(CMemoryException,pe)

{

printf("caughtmemoryexception\n");

}

AND_CATCH(CException,pe)//捕获剩下的MFC异常类对象

{

printf("caughtgenericexception\n");

pe->ReportError();

}

END_CATCH

}

int_tmain(intargc,TCHAR*argv[],TCHAR*envp[])

{

AfxWinInit(:

:

GetModuleHandle(NULL),NULL,:

:

GetCommandLine(),0);

Func3();

}

例:

使用自定义的MFC异常派生类:

classCMyException:

publicCException

{

public:

virtualBOOLGetErrorMessage(LPTSTRlpszError,UINTnMaxError,PUINTpnHelpContext)

{

ASSERT(lpszError!

=NULL&&AfxIsValidString(lpszError,nMaxError));

charszError[]="我的错误处理信息!

";

strncpy(lpszError,szError,nMaxError-1);

lpszError[nMaxError-1]=0;

returnTRUE;

}

};

voidFunc()

{

THROW(newCMyException);

}

int_tmain(intargc,TCHAR*argv[],TCHAR*envp[])

{

AfxWinInit(:

:

GetModuleHandle(NULL),NULL,:

:

GetCommandLine(),0);

TRY

{

Func();

}

CATCH(CException,pe)

{

pe->ReportError();

}

END_CATCH

return0;

}

4.Win32结构化异常处理(SEH)

Windows95、Windows98和Windows2000(即以前的WindowsNT)支持一种称为结构化异常处理的可靠的异常处理方法,此方法涉及与操作系统的协作,并且在编程语言中具有直接支持。

“SEH异常”是意外的或是使进程不能正常进行的事件。

硬件和软件都可以检测出异常。

硬件异常包括被零除和数值类型溢出等。

软件异常包括通过调用RaiseException函数检测到并发出信号通知系统的情况,以及由Windows检测到的特殊情况。

可以使用结构化异常处理编写更可靠的代码。

可以确保资源(如内存块和文件)在发生意外终止事件时正常关闭。

还可以利用简洁的、不依靠goto语句或详细测试返回代码的结构化代码来处理具体问题(如内存不足)。

注意:

结构化异常处理文章描述C编程语言的结构化异常处理。

虽然结构化异常处理也可用于C++,但对于C++程序应使用C++异常处理。

SEH是系统一级的异常处理,当建立标准C++异常处理时,编译器最终将它翻译成SEH异常处理。

SEH实际包含两个主要功能,结束处理和异常处理。

分别通过__try{}__finally{}结构和__try{}__except(){}结构实现。

4.1.__try{}__finally{}结构

此结构能够确保__try{}块内的代码执行完毕后,进入__finally{}块中执行一些清理工作。

例:

intFunc()

{

FILE*f=NULL;

__try{

f=fopen("abc.txt","wb");

//执行一些文件处理工作

if(fwrite("aaa",1,3

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 成人教育 > 远程网络教育

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1