非淡泊无以明志,非宁静无以致远
全部博文(408)
分类: C/C++
2010-12-07 17:25:22
1.MFC异常处理
MFC中异常处理的语法和语义构建在标准C++异常处理语法和语义的基础之上,其解决方案为:
MFC异常处理 = MFC 异常处理类 + 宏
1.1宏
MFC定义了TRY、CATCH(及AND_CATCH、END_CATCH)和THROW(及THROW_LAST)等用于异常处理的宏,其本质上也是标准C++的try、catch和throw的进一步强化,由这些宏的定义可知:
#ifndef _AFX_OLD_EXCEPTIONS
#define TRY { AFX_EXCEPTION_LINK _afxExceptionLink; try {
#define CATCH(class, e) } catch (class* e) \
{ ASSERT(e->IsKindOf(RUNTIME_CLASS(class))); \
_afxExceptionLink.m_pException = e;
#define AND_CATCH(class, e) } catch (class* e) \
{ ASSERT(e->IsKindOf(RUNTIME_CLASS(class))); \
_afxExceptionLink.m_pException = e;
#define END_CATCH } }
#define THROW(e) throw e
#define THROW_LAST() (AfxThrowLastCleanup(), throw)
// Advanced macros for smaller code
#define CATCH_ALL(e) } catch (CException* e) \
{ { ASSERT(e->IsKindOf(RUNTIME_CLASS(CException))); \
_afxExceptionLink.m_pException = e;
#define AND_CATCH_ALL(e) } catch (CException* e) \
{ { ASSERT(e->IsKindOf(RUNTIME_CLASS(CException))); \
_afxExceptionLink.m_pException = e;
#define END_CATCH_ALL } } }
#define END_TRY } catch (CException* e) \
{ ASSERT(e->IsKindOf(RUNTIME_CLASS(CException))); \
_afxExceptionLink.m_pException = e; } }
这些宏在使用语法上,有如下特点:
(1)用TRY 块包含可能产生异常的代码;
(2)用CATCH块检测并处理异常。要注意的是,CATCH块捕获到的不是异常对象,而是指向异常对象的指针。此外,MFC靠动态类型来辨别异常对象;
(3)可以在一个TRY 块上捆绑多个异常处理捕获块,第一次捕获使用宏CATCH,以后的使用AND_CATCH,而END_CATCH则用来结束异常捕获队列;
(4)在异常处理程序内部,可以用THROW_LAST 再次抛出最近一次捕获的异常。
1.2 MFC 异常处理类
MFC较好地将异常封装到CException类及其派生类中,自成体系,下表给出了MFC 提供的预定义异常:
异常类 含义
CMemoryException 内存不足
CFileException 文件异常
CArchiveException 存档/序列化异常
CNotSupportedException 响应对不支持服务的请求
CResourceException Windows 资源分配异常
CDaoException 数据库异常(DAO 类)
CDBException 数据库异常(ODBC 类)
COleException OLE 异常
COleDispatchException 调度(自动化)异常
CUserException 用消息框警告用户然后引发一般 CException 的异常
标准C++的异常处理可以处理任意类型的异常,而3.1节的MFC 宏则只能处理CException 的派生类型,下面我们看一个CFileException的使用例子:
#include
#include "afxwin.h"
int main()
{
TRY
{
CFile f( "d:\\1.txt", CFile::modeWrite );
}
CATCH( CFileException, e )
{
if( e->m_cause == CFileException::fileNotFound )
cout << "ERROR: File not found\n" << endl;
}
}
要想这个程序能正确地执行,我们可以在第一个__try块的外面再套一个__try块和一个接收filter-expression返回值为EXCEPTION_EXECUTE_HANDLER的__except块,
程序改为:
//例4-2
#include "stdio.h"
void main()
{
int* p = NULL; // 定义一个空指针
puts("SEH begin");
__try
{
__try
{
puts("in try");
__try
{
puts("in try");
*p = 0; // 引发一个内存访问异常
}
__finally
{
puts("in finally");
}
}
__except(puts("in filter"), 0)
{
puts("in except");
}
}
__except(puts("in filter"), 1)
{
puts("in except");
}
puts("SEH end");
}
程序输出:
SEH begin
in try //执行__try块
in try //执行嵌入的__try块
in filter1 //执行filter-expression,返回EXCEPTION_CONTINUE_SEARCH
in filter2 //执行filter-expression,返回EXCEPTION_EXECUTE_HANDLER
in finally //展开嵌入的__finally
in except2 //执行对应的__except块
SEH end //处理完毕
由此可以看出,因为第一个__except的filter-expression返回EXCEPTION_CONTINUE_SEARCH 的原因,"in except1"没有被输出。程序之所以没有崩溃,是因为最终碰到了接收EXCEPTION_EXECUTE_HANDLER的第2个__except。
SEH使用复杂的地方在于较难控制异常处理的流动方向,弄不好程序就"挂"了。
如果把例4-1中的__except(puts("in filter"), 1)改为__except(puts("in filter"), -1),程序会进入一个死循环,输出:
SEH begin
in try //执行__try块
in try //执行嵌入的__try块
in filter //执行filter-expression,返回EXCEPTION_CONTINUE_EXECUTION
in filter
in filter
in filter
in filter
…//疯狂输出"in filter"
最后疯狂地输出"in filter",我们把断点设置在__except(puts("in filter"), -1)语句之前,按F5会不断进入此断点。
2.各种异常处理的对比
下表给出了从各个方面对这本文所给出的Visual C++所支持的四种异常处理进行的对比:
异常处理 支持语言 是否标准 复杂度 推荐使用
C异常处理 C语言 标准C 简单 推荐
C++异常处理 C++语言 标准C++ 较简单 推荐
MFC异常处理 C++语言 仅针对MFC程序 较简单 不推荐
SEH异常处理 C和C++语言 仅针对Microsoft编译环境 较复杂 不推荐
本文所讲解的仅仅是Visual C++异常处理的初步知识,对于更深入的内容,还需要我们在不断的编程过程中去领悟和学习。