`
fpwjp
  • 浏览: 45967 次
  • 性别: 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
分享到:
评论

相关推荐

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

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

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

    MySQL中的异常处理是数据库编程中不可或缺的一部分,它允许开发者预设对可能出现的错误或异常的响应,从而确保程序的稳定性和健壮性。在MySQL中,异常定义和处理主要是通过`DECLARE`语句来实现的。 1. **异常定义**...

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

    异常处理是编程中的一种机制,用于捕获和处理运行时发生的错误或异常情况。异常可以由硬件引发,如硬件异常,也可以由操作系统或应用程序自身触发,即软件异常。当异常发生时,操作系统允许程序有机会检查异常类型并...

    易语言线程结构异常处理

    在易语言中,线程是并发执行的程序单位,线程结构异常处理是编程过程中非常重要的一环,因为线程可能会遇到各种异常情况,如内存访问错误、除零异常等。 线程结构异常处理源码是易语言中处理这些异常的关键部分。当...

    java异常处理习题

    Java 异常处理习题 Java 异常处理是 Java 编程语言中的一种重要机制,用于处理程序在运行时可能出现的错误或异常情况。下面是关于 Java 异常处理的习题和知识点总结: 一、Java 异常处理关键字 * Java 中用来抛出...

    易语言HOOK异常处理

    "New_SE_Handler"可能是一个新的结构化异常处理程序,结构化异常处理(SEH)是Windows操作系统中的一个特性,用于处理硬件和软件异常。 "GetSeAddr"可能是获取异常发生时的地址函数,这对于分析异常原因和定位问题...

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

    在编程领域,异常处理是确保程序健壮性与稳定性的关键技术。对于C、C++以及基于MFC(Microsoft Foundation Classes)的开发来说,异常处理更是不可或缺的一部分。本篇文章将深入浅析C、C++中的异常处理机制以及MFC中...

    详解SpringCloud Finchley Gateway 统一异常处理

    详解 SpringCloud Finchley Gateway 统一异常处理 SpringCloud Finchley Gateway 统一异常处理是指在使用 SpringCloud Finchley 版本的 Gateway 时,如何统一处理系统级异常的方法。默认情况下,SpringCloud ...

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

    ### C++异常处理详解 #### 引言 随着软件项目的规模不断扩大,确保软件质量变得日益复杂且至关重要。软件产品在实际应用中面临多种潜在风险,包括但不限于程序崩溃、数据丢失等,这些问题严重影响用户体验及系统...

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

    【异常处理】是编程中必不可少的一个环节,尤其是在Java这样的面向对象语言中。异常处理机制使得程序在遇到错误时能够优雅地中断执行流程,提供错误信息,并有机会进行恢复操作,而不是简单地崩溃。以下是对异常处理...

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

    异常处理是Java编程中至关重要的一个概念,它确保了程序在遇到错误或异常情况时能够以优雅的方式继续执行或者终止。下面是对Java异常处理机制的详细解析。 在Java中,异常是程序运行时发生的错误,它中断了正常的...

    ARM处理器异常处理步骤

    ARM处理器异常处理是指ARM微处理器对各种异常情况作出响应和处理的过程。异常指的是处理器在正常执行程序时遇到的特殊情况,例如外部中断请求、未对齐的内存访问错误、指令预取终止等。为了保证系统的稳定性和正确性...

    游标和异常处理 游标和异常处理

    游标和异常处理 游标是 SQL 的一个内存工作区,由系统或用户以变量的形式定义。游标的作用就是用于临时存储从数据库中提取的数据块。在某些情况下,需要把数据从存放在磁盘的表中调到计算机内存中进行处理,最后将...

    Power builder9异常处理

    在 PowerBuilder 9(简称 PB9)开发过程中,异常处理是一项关键的技术,它能帮助开发者有效地管理和解决程序中出现的错误,确保系统的稳定运行。在实际应用中,开发者经常会遇到各种预知和不可预知的问题,如系统级...

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

    《ADS异常处理详解》 异常处理是嵌入式系统中至关重要的部分,特别是在基于ARM架构的系统中。本文将深入探讨ARM处理器的异常处理机制,包括异常类型、处理流程、异常优先级以及向量表等内容。 一、异常类型 ARM...

    java实验报告4-异常处理

    Java异常处理是编程中至关重要的一个环节,它确保了程序在遇到错误时能够优雅地运行,而不是突然崩溃。本实验报告“java实验报告4-异常处理”旨在帮助初学者掌握Java中的异常处理机制,以及如何利用log4j进行日志...

    reactnative异常处理库

    React Native 异常处理库是专门为在React Native框架下开发的混合移动应用提供错误管理和调试支持的工具。React Native允许开发者使用JavaScript编写原生移动应用,但JavaScript代码的运行环境中可能会遇到各种错误...

    Spring Cloud Gateway的全局异常处理

    ### Spring Cloud Gateway全局异常处理详解 #### 一、引言 在微服务架构中,网关作为服务入口,承担着路由转发、限流熔断、鉴权认证等职责。Spring Cloud Gateway作为一款基于Spring Framework 5、Project Reactor...

    两数计算+异常处理

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

    易语言SEH异常处理源码.rar

    在这个"易语言SEH异常处理源码.rar"压缩包中,包含的主要是易语言的源代码,涉及到的是系统异常处理的相关技术,特别是结构化异常处理(Structured Exception Handling,简称SEH)。 结构化异常处理是Windows操作...

Global site tag (gtag.js) - Google Analytics