`
fpwjp
  • 浏览: 45094 次
  • 性别: Icon_minigender_1
  • 来自: 河北
社区版块
存档分类
最新评论

异常处理

    博客分类:
  • C++
阅读更多

      异常(exception)是指在程序运行过程中发生的非常事件,异常的发生将中断程序的正常运行,将控制权交给操作系统。一般情况下,操作系统将给出一些提示信息,然后关闭发生异常的程序。但也可能由于无法恢复的错误,如无限的资源分配而产生的资源漏洞,造成系统崩溃。异常处理是指在编程时就预先考虑到各种可能出现的异常情况,当程序运行逻辑偏离正常运行方向时进行的容错处理,如给出提示信息、保存用户数据及释放系统资源等。

      1.C++异常处理

       异常可能源于程序本身的设计错误,也可能源于系统软硬件故障。一个优秀的程序员在编写程序时,应考虑到用户在实际操作过程中可能发生的误操作和可能出现的系统故障,对程序执行过程中出现的各种错误进行处理。如用户非法的输入、要操作的数据库名不正桷、盘驱出错、除数为0、浮点溢出及无效的内存地址等。

      事实上,程序员在进行程序设计时就会自然想到利用if-else语句进行错误处理。例如,当打开一个文件时,可能出现文件打开不成功的情况,如文件名或路径不正确。此时,应该给出一些错误提示信息。下面是一段打开文件操作的程序代码,其中就考虑了文件打不成功的情况。

CFile f1
if(f1.Open("c:\\MyDocuments\\redme.txt",CFile::ModeRead))
{
      ...                           //文件打开成功
}
else
{
    cout <<"Error opening file!\n";  //文件打开不成功
}

     采用if-else分支结构处理异常的缺点是不合适处理大量的异常,更不能处理不可预知的异常。C++语言提供了专门的try-catch异常处理语句,采用这种结构简化了异常处理的流程,并能处理不可预知的异常。try-catch结构的一般形式如下:

try                                      //发现异常
{
          ...                            //可以使用throw语句抛出异常
}
catch(异常类型[,参数])        //捕获异常,可以有多个catch块
{
         ...                             //处理异常
}

     与if-else结构相比,try-catch异常处理的逻辑结构非常清晰,并且能在一个catch块中处理多个异常。编程时将可能发生异常的语句放在try块中,将处理异常的语句放在catch块中。当程序运行时,如果在try块中发生了异常,就会抛出一个异常,这时程序就会改变执行顺序,转为执行catch块。catch块按收异常,并对异常进行处理。catch块内的代码只能由异常的引发而执行,不能通过其他方式执行异常处理代码。

      一般而言,catch块中异常处理主要包括以下五个方面

      (1)给出正确操作的提示信息,并且在退出程序之前保存可能会被破坏的数据

      (2)根据throw语句传递的异常信息进行了相应的处理。

      (3)对于处理不了的异常,尽可能将它转变成能够另一个异常处理程序处理的形式,并抛出该异常。

      (4)重新尝试执行最初的语句。

      (5)如果需要可以返回一个有效值给主调函数。

      例1 编写一个Win32控制台程序OpenFile,模拟文件操作时的异常处理。

      [编程说明与实现]

      利用Win32 Console Application向导建立一个控制台项目OpenFile,并加一个C++源文件,其源程序如下:

#include "stdafx.h"
#include <afx.h>
#include <iostream.h>

int main(int argc, char* argv[])
{
	CFile f1,f2,f3;
	BOOL b1(FALSE),b2(FALSE),b3(FALSE);
	try
	{
		b1=f1.Open("C:\\My Documents\\readme1.txt",CFile::modeRead);
		if(!b1) throw "Error opening readme1.txt!"; //引发一个异常,执行catch块
		
		b2=f2.Open("C:\\My Documents\\readme2.txt",CFile::modeRead);
		if(!b2) throw "Error opening readme2.txt!"; //引发一个异常,执行catch块
		
		b3=f3.Open("C:\\My Documents\\readme3.txt",CFile::modeRead);
		if(!b3) throw "Error opening readme3.txt!"; //引发一个异常,执行catch块
	}
	catch(char* pszError)							//异常处理
	{
		cout <<pszError<<"\n";
	}

	if(b1)
	{
		cout <<"Success opening readme1.txt!\n";
		f1.Close();									//关闭文件
	}

	if(b2)
	{
		cout <<"Success opening readme2.txt!\n";
		f2.Close();									//关闭文件
	}

	if(b3)
	{
		cout <<"Success opening readme3.txt!\n";
		f3.Close();									//关闭文件
	}
	return 0;
}

      在C++程序中使用了MFC的类,需要对项目进行有关设置:执行"工程|设置"命令,在"常规"页面设置Microsoft基础类项,可以将该项设置为Use MFC in a Shared DLL或Use MFC in a Static Library。然后执行编译、链译命令(F7)即可得到运行程序,程序运行后能够在控制台窗口输出有关的异常信息。

      需要说明的是例1中的异常处理只是在catch块中输出出错信息。如果需要完成释放资源等清理工作,可以在catch块中添加相应的代码。在catch块执行后,执行流程传到catch块后的第1条语句,即执行if(b1){...}。

      2.Win32异常处理

      应用程序中发生的异常也可能源于操作系统,如除数为0、越界访问内存和运算结果溢出等,这些异常称为Win32异常。当Win32异常发生时,如果程序没有提供对应的异常处理块,Windows就会出一个信息对话框,告诉用户由于非法操作程序将被关闭。下面的例子将产生一个由于除数为0而引发的Win32异常,为了观察Windows对Win32异常的处理过程,程序并没有进行异常处理。

      例2 编写一个单文档应用程序Win32Expt,在OnDraw()函数中产生一个因除数为0而引发的Win32异常。

      [编程说明与实现]

      利用向导创建一个SDI应用程序Win32Expt,在OnDraw()函数编写如下代码:

void CWin32ExptView::OnDraw(CDC* pDC)
{
	CWin32ExptDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	// TODO: add draw code for native data here
	int iNum,nZero;
	nZero=0;
	iNum=1/nZero;
}

      编译、链接并运行程序Win32Expt,屏幕上出现一个信息对话框,告诉用户非法操作该程序将被关闭。

      例2程序Win32Expt中因除数为0而引发的Win32异常由Windows处理的,程序员可以在程序中设置自已的异常处理块,这样就能截获Win32异常并进行处理,避免由于除数为0而引起程序的关闭。

      例3 修改例2中的程序Win32Expt,当发生除数为0的Win32异常显示程序给出的信息对话框。

      [编程说明与实现]

      区别于例2,由于除数为0异常是源于Windows,在try块中无须使用throw语句抛出异常。只需将可能发生异常的语句放在try块中,并添加catch异常处理块。

void CWin32ExptView::OnDraw(CDC* pDC)
{
	CWin32ExptDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	// TODO: add draw code for native data here
	try{
		int iNum,nZero;
		nZero=0;
		iNum=1/nZero;
	}
	catch(...)		//catch块可以没有参数
	{
		MessageBox("Integer divide by zero!","Win32 Exception",MB_ICONERROR);
	}
}

     程序Win32Expt运行后将同时出现一个如图1所示的信息对话框,其标题和内容都是程序给出的。关闭该对话框,程序Win32Expt并没终止运行。 
 

      例2中程序Win32Expt还有一个不足,不管发生哪一种异常,它都给出同一个信息对话框,程序并没有确定发生了哪一种异常。

      在Windows.h文件中定义了Win32异常类型代码。表1列出了常见的Win32异常类型代码及其说明。可定义一个Win32异常类型翻译函数,将异常类型代码翻译成代表其含义的字符串。例3说明了异常类型翻译函数的定义和使用方法。

 

      例4 修改例3的程序WinExpt,在程序中确定发生的Win32异常类型,并显示对应内容的信息对话框。

      [编程说明与实践]

      (1)在Win32ExptView.cpp源文件中编写一个异常类型翻译函数ThrowExpt(),函数名和参数名可以任意,但其类型不能任意。在函数ThrowExpt()中根据异常类型代码nExCode再抛出一个异常,其参数是说明异常类型的字符串。该异常被OnDraw()函数中catch块处理。注意,函数ThrowExpt()应放在OnDraw()函数的前面。

void ThrowExpt(unsigned int nExCode,EXCEPTION_POINTERS * ptrExPtra)
{
	switch(nExCode)
	{
		case EXCEPTION_INT_DIVIDE_BY_ZERO:
			throw "Integer divide by zero!";
			break;
		case EXCEPTION_INT_OVERFLOW:
			throw "Integer overflow!";
			break;
		case EXCEPTION_FLT_OVERFLOW:
			throw "Float overflow!";
			break;
		default:
			throw "Other exception!";
	}
}

     (2)在OnDraw()函数中,通过调用_set_se_translator()函数将异常类型翻译函数的函数指针传递给Windows。

void CWin32ExptView::OnDraw(CDC* pDC)
{
	CWin32ExptDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	// TODO: add draw code for native data here
	_set_se_translator(ThrowExpt);		//传递异常类型翻译的函数指针
	
	try{
		int iNum,nZero;
		nZero=0;
		iNum=1/nZero;
	}
	catch(char* pszExceptionName)
	{
		//不同类型的异常给出不同的pszException字符串内容
		MessageBox(pszExceptionName,"Win32 Exception",MB_ICONERROR);
	}
}

     这样,程序Win32Expt就能够确定发生的Win32异常类型,可进行不同的处理,但要注意,_set_se_translator()函数是在eh.h头文件中声明,因此必须在Win32ExptView.h头文件中包含eh.h头文件:#include<eh.h>。

     3.MFC异常宏和异常类

     作为对C++异常处理结构try-catch的改进,MFC以宏的形式支持异常处理功能,主要定义了MFC异常宏和MFC异常类。MFC异常宏将发生的异常情况与MFC类联系在一起,能够分门别类地检测并抛出不同类型的异常,以便程序员进行不同的处理。MFC异常宏和异常类使异常处理更灵活,可处理的异常类型更广泛,可执行性更好。

     MFC异常宏的语法结构如下:

TRY
{
       ...                       //引发异常
}
CATCH(ExceptionClass1,pe)
{
      ...                       //异常类1的处理代码
}
AND_CATCH(ExceptionClass2,pe)     //AND_CATCH块可有多个或省略
{
     ...                     //异常类2的处理代码
}
END_CATCH

    与C++中的try-catch结构相比,MFC异常宏能让用户获取有关错误原因的更为详细信息。CATCH宏有两个参数,参数ExceptionClassN指定与异常情况对应的异常类(见表2);参数pe指向异常类的对象,该对象由MFC异常宏建立,用户不需要声明。可以通过指针pe访问异常对象,获取异常信息。

     常用的MFC异常类如表2所示。对于MFC异常类,用户可以直接使用它们,也可以根据MFC异常基类CException派生自已的异常类。

  

          MFC异常宏与try-catch结构一样,异常需要引发才能进入到CATCH块中进行处理。MFC提供以下两种引发异常类型。

      (1)由于执行TRY块中产生异常的语句而引发的异常。这种异常具有一定特征值,它的引发是自协的,发生异常时会自动进入对应的异常类进行处理。

      (2)使用THROW宏或者AfxThrowXXXException()函数直接抛出异常,这些异常是与类相关的。THROW宏的参数是一个指向异常对象(CException的派生类)的指针。XXXX表示与异常情况对应的异常类。

      4.MFC异常处理

      MFC定义了异常宏和一系列异常类,并提供了THROW宏和AfxThrowXXXXException()函数。因此,编程时如果需要进行异常处理,可以在TRY块中根据具体情况,利用THROW宏或AfxThrowXXXXException()函数抛出不同的异常,从而执行对应的异常类的处理代码。本节主要以MFC文件异常处理为例介绍MFC异常片处理的方法。

     MFC提供一个有关文件系统的异常类CFileException,CFileException包含一些公共数据成员和成员函数,用于判断出现什么类型异常,哪个文件出现异常。

      CFileException类的定义如下:

class CFileException:public CException
{
	DECLARE_DYNAMIC(CFileException)
public:
	enum{					//m_cause的值
		none,				//没有发生的异常
		generic,			//未说明的一般错误
		fileNotFound,		//没有发现文件
		badPath,			//无效的路径
		tooManyOpenFiles,	//打开的文件过多
		accessDenied,		//不能访问文件
		invalidFile,		//文件句柄无效
		removeCurrentDir,	//试图删除当前工作目录
		directoryFull,		//目录已满
		badSeek,			//不能设置读写指针
		hardIO,				//硬件错误
		sharingViolation,	//共享区被锁
		lockViolation,		//试图锁住一个已经加锁的区域
		diskFull,			//磁盘空间不足
		endOfFile			//已到文件结尾
	};

	CFileException(int cause=CFileException::none,LONG lOsError=-1,
										LPCSTR lpszArchiveName=NULL);

	//Attribute
	int m_cause;			//通用的错误代码,对应集合enum的枚举值
	LONG m_lOsError;		//与操作系统有关的错误代码
	CString m_strFileName;	//与异常相关的文件名
	//Operations
	//convert a OS dependent error to a Cause。把错误代码转换成构造函数所要求的格式
	static int PASCAL OsErrorToException(LONG lOsError);
	static int PASCAL ErrnoToException(int nErrno);
	//helper function to throw exception after convertion to a Cause。抛出一个异常
	static void PASCAL ThrowOsError(LONG IOsError,LPCTSTR lpszFileName=NULL);
	static void PASCAL ThrowErrno(int nErrno,LPCTSTR lpszFileName=NULL);
public:
	virtual ~CFileException()
	...
};

 

     m_cause存放通用的错误代码,其值是CFileException类中集合enum的枚举值,其含义请参扯上述类定义中注释。m_IosError存放与操作系统有关的错误代码,其含义请参看操作系统的技术手册。数据成员m_strFileName存放与异常相关的文件名。

     成员函数OsErrorToException()和ErrnoToException()用于把异型类型代码转换成构造函数所要求的格式。ThrowOsError()和ThrowError()函数以前的异常类型代码为基础抛出一个文件异常。

     建议用全局函数AfxThrowFileException()抛出CFileException异常,该函数的声明如下所示。

void AfxThrowFileException(int cause,LONG IosError=-1,LPCTSTR lpszFileName=NULL);

    函数参数cause、IosError和LpszFileName的含义与前述类似。调用该函数时,第二个和第三个实参可以省略。

    例5 以文件异常处理为例说明如何利用MFC宏和异常类来进行异常处理。建立一个单文档应用程序MyFile,当执行“文件”菜单的“打开文本文件”命令时,打开一个对话框,在对话框中输入文件名。程序要求打开文件名的后缀是TXT。文件打开成功后给出打开成功的信息提示信息,出现异常情况时进行相应的异常处理。

     [编程说明与实现]

     (1)首先利用MFC AppWizard向导创建一个单文档应用程序MyFile。利用Insert|Resource命令创建一个对话框资源,添加对话框控件,如一个Edit Box控件。利用ClassWizard类向导创建一个对话框类CFileDlg,为Edit Box控件定义CString类型的数据成员m_filename。

     (2)将"文件"菜单中的“打开”菜单项改为“打开文本文件”。利用ClassWizard为该菜单命令ID_FILE_OPEN添加消息处理函数CMyFileView::OnFileOpen()。在源文件MyFileView.cpp中包含FileDlg.h头文件。OnFileOpen()函数如下:

void CMyFileView::OnFileOpen() 
{
	// TODO: Add your command handler code here
	CString strFile,strFileR4;
	CString strMessage;
	CFile myFile;
	CFileDlg dlg;
	if(dlg.DoModal())
	{
		TRY
		{
			strFile=dlg.m_filename;		//得到要打开的文件名
			strFileR4=strFile.Right(4);
			strFileR4.MakeUpper();
			if(strFileR4.Compare(".TXT")!=0)	//引发异常
				AfxThrowFileException(CFileException::generic);
			if(!myFile.Open(strFile,CFile::modeRead))
				AfxThrowFileException(CFileException::fileNotFound);
			MessageBox("Open successfully!");
			myFile.Close();
		}
		CATCH(CFileException,pe)
		{
			switch(pe->m_cause)
			{
			case CFileException::generic:
				strMessage=strFile+" file is a non_TXT file!";	//文件类型不正确
				break;
			case CFileException::fileNotFound:
				strMessage=strFile+" file isn't found!";		//文件不存在
				break;
			default:
				strMessage="Error of not captured!";			//未捕获的文件错误
			}
			MessageBox(strMessage,"File Exception");
		}
		END_CATCH
	}
}

      首先将ReadMe.txt文本文件复制到MyFile程序所在目录,运行程序后执行“文件|打开文本文件”命令弹出一个对话框,在对话框中输入不同的文件名(包括后缀),就能看到程序具有所要求的异常处理功能。

      在MyFile程序中简单处理了三个方面的异常:

      (1)CFileException::generic异常。

      (2)CFileException::fileNotFound异常。

      (3)其它非上述两异常的异常。

      如是希望对文件其它类型的异常处理,可以参照上述方法,首先分析可能发生的异常类型,再编写相应的异常处理代码。其它MFC异常类如CMemoryException、CResourceException等也是经常需要用到的异常类,其使用方法与CFileException异常类大同小异,可参阅它们的类定义。

  • OpenFile.rar (1.1 MB)
  • 描述: try-catch的实例
  • 下载次数: 1
  • Win32Expt.rar (1.8 MB)
  • 描述: Win32异常处理实例1
  • 下载次数: 1
  • 描述: 程序给出的信息对话框
  • 大小: 6.8 KB
  • 描述: 常见的Win32异常类型
  • 大小: 5.6 KB
  • Win32Expt.rar (1.8 MB)
  • 描述: Win32异常处理实例2
  • 下载次数: 2
  • Win32Expt.rar (1.9 MB)
  • 描述: Win32异常处理实例3
  • 下载次数: 1
  • 描述: 常用的MFC异常类
  • 大小: 7.3 KB
  • MyFile.rar (1.9 MB)
  • 描述: MFC异常处理的实例
  • 下载次数: 2
分享到:
评论

相关推荐

    Spring Cloud Gateway的全局异常处理

    Spring Cloud Gateway的全局异常处理 Spring Cloud Gateway中的全局异常处理不能直接用@ControllerAdvice来处理,通过跟踪异常信息的抛出,找到对应的源码,自定义一些处理逻辑来符合业务的需求。 网关都是给接口做...

    C#异常处理总结及简单实例

    C#异常处理总结及简单实例 一、异常处理的理解? 异常处理是指程序在运行过程中,发生错误会导致程序退出,这种错误,就叫做异常。 因此处理这种错误,就称为异常处理。 二、异常处理如何操作? C# 异常处理时建立在...

    异常处理.ppt异常处理.ppt异常处理.ppt异常处理.ppt异常处理.ppt

    异常处理.ppt异常处理.ppt异常处理.ppt异常处理.ppt异常处理.ppt异常处理.ppt

    两数计算+异常处理

    课程作业,实现两数计算及其异常处理,异常处理机制能让程序在异常发生时,按照代码的预先设定的异常处理逻辑,针对性地处理异常,让程序尽最大可能恢复正常并继续执行,且保持代码的清晰。 Java中的异常可以是函数...

    精讲RestTemplate自定义请求失败异常处理.docx

    在开始进行自定义的异常处理逻辑之前,我们有必要看一下异常处理的默认实现。也就是:为什么会产生上面小节提到的现象? ResponseErrorHandler是RestTemplate请求结果的异常处理器接口 o接口的第一个方法hasError...

    异常处理 异常处理 异常处理

    关于异常处理的word文档 关于异常处理的word文档 关于异常处理的word文档

    Springboot全局异常处理demo.zip

    Springboot全局异常处理demo 项目的开发中,不管是对底层的数据库操作过程,还是业务层的处理过程,还是控制层的处理过程,都不可避免会遇到各种可预知的、不可预知的异常需要处理。每个过程都单独处理异常,系统的...

    MySQL定义异常和异常处理详解

    主要为大家详细介绍了MySQL定义异常和异常处理,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

    C和C++中的异常处理

    2. Microsoft 对异常处理方法的扩展 3. 标准 C++异常处理的基本语法和语义 4. 实例剖析 EH 5. C++的 new 和 delete 操作时的异常处理 6. Microsoft 对于的实现版本中的异常处理 7. 部分构造及 placement delete 8. ...

    C++ 异常处理 C++ 异常处理

    C++ 异常处理 C++ 异常处理C++ 异常处理C++ 异常处理C++ 异常处理C++ 异常处理C++ 异常处理C++ 异常处理

    java异常处理java异常处理

    java异常处理java异常处理java异常处理java异常处理java异常处理java异常处理java异常处理java异常处理java异常处理java异常处理java异常处理java异常处理java异常处理

    c/vc++/MFC异常处理/结构化异常处理 浅析

    c/vc++/结构化异常处理 浅析 C语言异常处理 C++语言异常处理 异常处理函数 MFC异常处理 结构化异常处理

    spingmvc+mybatis+统一异常处理机制

    统一异常处理会区分前端是否ajax请求,自动返回json数据格式,要求开发人员在处理ajax请求时统一封装成一个对象返回,以符合代码统一规范。 此工程在idea环境编写,导入请自己新建工程手工复制代码导入。

    ADS异常处理.pptADS异常处理.pptADS异常处理.ppt

    ADS异常处理.pptADS异常处理.pptADS异常处理.ppt

    ARM异常处理机制ARM异常处理机制

    ARM异常处理机制ARM异常处理机制ARM异常处理机制ARM异常处理机制ARM异常处理机制ARM异常处理机制ARM异常处理机制ARM异常处理机制ARM异常处理机制

    详解SpringCloud Finchley Gateway 统一异常处理

    主要介绍了详解SpringCloud Finchley Gateway 统一异常处理,非常具有实用价值,需要的朋友可以参考下

    工作流系统异常处理实现方法

    中的异常处理问题显得尤为突出[1-3]。传统上将异常处理包含于正常流程中的方法不仅不能 有效的处理各类异常,同时使得整个系统流程复杂化;而完全的人工参与也使得异常处理过 程效率低下,形式极不规范。所以,工作...

    第7章 java异常处理

    第7章 java异常处理

    SQLserver存储过程异常处理.txt

    SQLserver存储过程异常处理

    异常处理机制知识点小总结

    这是自己通过看书对java异常处理的一些总结

Global site tag (gtag.js) - Google Analytics