[转] c++ try catch 问题

windhaunting原文地址java

之前都是用try{} catch(…){}来捕获C++中一些意想不到的异常, 今天看了Winhack的帖子才知道,这种方法在VC中实际上是靠不住的。例以下面的代码:linux

    try
    {
    BYTE* pch ;
    pch = ( BYTE* )00001234 ;   //给予一个非法地址
    *pch = 6 ; //对非法地址赋值,会形成Access Violation 异常
    }
    catch(...)
    {
    AfxMessageBox( "catched" ) ;
    } 

这段代码在debug下没有问题,异常会被捕获,会弹出”catched”的消息框。 但在Release方式下若是选择了编译器代码优化选项,则VC编译器会去搜索try块中的代码, 若是没有找到throw代码, 他就会认为try catch结构是多余的, 给优化掉。 这样形成在Release模式下,上述代码中的异常不能被捕获,从而迫使程序弹出错误提示框退出。程序员

那么可否在release代码优化状态下捕获这个异常呢, 答案是有的。 就是__try, __except结构, 上述代码若是改为以下代码异常便可捕获。web

    __try
    {
    BYTE* pch ;
    pch = ( BYTE* )00001234 ;   //给予一个非法地址
    *pch = 6 ; //对非法地址赋值,会形成Access Violation 异常
    }
    __except( EXCEPTION_EXECUTE_HANDLER )
    {
    AfxMessageBox( "catched" ) ;
    } 

可是用__try, __except块还有问题, 就是这个不是C++标准, 而是Windows平台特有的扩展。 并且若是在使用过程当中涉及局部对象析构函数的调用,则会出现C2712 的编译错误。 那么还有没有别的办法呢?编程

固然有, 就是仍然使用C++标准的try{}catch(..){}, 但在编译命令行中加入 /EHa 的参数。这样VC编译器不会把try catch模块给优化掉了。小程序

找到一篇比较好的英文文章谈这个问题: http://members.cox.net/doug_web/eh.htm数组

用C++10 年多了 , 竟然这么基础的问题都搞错, 真是汗颜。 要加紧学习啊, Stay Hungry, Stay Foolish!安全

Written by oldmonk on 九月 11th, 2006 with 2 comments.
Read more articles on IT.
app

 


2. C++中catch(…)如何使用
上 一篇文章中详细讲了讲C++异常处理模型的trycatch使用语法,其中catch关键字是用来定义catch block的,它后面带一个参数,用来与异常对象的数据类型进行匹配。注意catch关键字只能定义一个参数,所以每一个catch block只能是一种数据类型的异常对象的错误处理模块。若是要想使一个catch block能抓获多种数据类型的异常对象的话,怎么办?C++标准中定义了一种特殊的catch用法,那就是” catch(…)”。 框架

感性认识

一、catch(…)究竟是一个什么样的东东,先来个感性认识吧!看例子先:

 

int main()
{
try
{
cout << "在 try block 中, 准备抛出一个异常." << endl;
//这里抛出一个异常(其中异常对象的数据类型是int,值为1)
throw 1;
}
//catch( int& value )
//注意这里catch语句
catch( …)
{
cout << "在 catch(…) block 中, 抛出的int类型的异常对象被处理" << endl;
}
}

二、哈哈!int类型的异常被catch(…)抓获了,再来另外一个例子:

int main()
{
try
{
cout << "在 try block 中, 准备抛出一个异常." << endl;
//这里抛出一个异常(其中异常对象的数据类型是double,值为0.5)
throw 0.5;
}
//catch( double& value )
//注意这里catch语句
catch( …)
{
cout << "在 catch(…) block 中, double类型的异常对象也被处理" << endl;
}
}

三、一样,double类型的异常对象也被catch(…)块抓获了。是的,catch(..)能匹配成功全部的数据类型的异常对象,包括C++语言提 供全部的原生数据类型的异常对象,如int、double,还有char*、int*这样的指针类型,另外还有数组类型的异常对象。同时也包括全部自定义的抽象数据类型。例程以下:

int main()
{
try
{
cout << "在 try block 中, 准备抛出一个异常." << endl;
//这里抛出一个异常(其中异常对象的数据类型是char*)
char* p=0;
throw p;
}
//catch( char* value )
//注意这里catch语句
catch( …)
{
cout << "在 catch(…) block 中, char*类型的异常对象也被处理" << endl;
}
}
//------------------------------------------------------------
int main() { try { cout << "在 try block 中, 准备抛出一个异常." << endl; //这里抛出一个异常(其中异常对象的数据类型是int[]) int a[4]; throw a; } //catch( int value[] ) //注意这里catch语句 catch( …) { cout << "在 catch(…) block 中, int[]类型的异常对象也被处理" << endl; } }

四、对于抽象数据类型的异常对象。catch(…)一样有效,例程以下:

class MyException
{
public:
protected:
int code;
};

int main()
{
try
{
cout << "在 try block 中, 准备抛出一个异常." << endl;
//这里抛出一个异常(其中异常对象的数据类型是MyException)
throw MyException();
}
//catch(MyException& value )
//注意这里catch语句
catch( …)
{
cout << "在catch(…) block中, MyException类型的异常对象被处理" << endl;
}
}

对catch(…)有点迷糊?
一、究竟对catch(…)有什么迷糊呢?仍是看例子先吧!

void main()
{
int* p = 0;

try
{
// 注意:下面这条语句虽然不是throw语句,但它在执行时会致使系统
// 出现一个存储保护错误的异常(access violation exception)
*p = 13; // causes an access violation exception;
}
catch(...)
{
//catch(…)能抓获住上面的access violation exception异常吗?
cout << "在catch(…) block中" << endl;
}
}

  请问上面的程序运行时会出现什么结果吗?catch(…)能抓获住系统中出现的access violation exception异常吗?朋友们!和咱们的主人公阿愚同样,本身动手去测试一把!
结 果又如何呢?实际上它有两种不一样的运行结果,在window2000系统下用VC来测试运行这个小程序时,发现程序能输出"在catch(…) block中"的语句在屏幕上,也即catch(…) 能成功抓获住系统中出现的access violation exception异常,很厉害吧!但若是这个一样的程序在linux下用gcc编译后运行时,程序将会出现崩溃,并在屏幕上输出”segment fault”的错误信息。

主人公阿愚有点急了,也开始有点迷糊了,为何?为何?为何一样一个程序在两种不一样的系统上有不一样的表现 呢?其缘由就是:对于这种因为硬件或操做 系统出现的系统异常(例如说被零除、内存存储控制异常、页错误等等)时,window2000系统有一个叫作结构化异常处理(Structured Exception Handling,SEH)的机制,这个东东太厉害了,它能和VC中的C++异常处理模型很好的结合上(实际上VC实现的C++异常处理模型很大程度上建 立在SEH机制之上的,或者说它是SEH的扩展,后面文章中会详细阐述并分析这个久富盛名的SEH,看看catch(…)是如何神奇接管住这种系统异常出 现后的程序控制流的,不过这都是后话)。而在linux系统下,系统异常是由信号处理编程方法来控制的(信号处理编程,signal processing progamming。在介绍unix和linux下如何编程的书籍中,都会有对信号处理编程详细的介绍,固然执著的主人公阿愚确定对它也不会放过,会深 入到unix沿袭下来的信号处理编程内部的实现机制,并尝试完善改进它,使它也可以较好地和C++异常处理模型结合上)。

那么C++标 准中对于这种同一个程序有不一样的运行结果有何解释呢?这里须要注意的是,window2000系统下catch(…)能捕获住系统异常, 这彻底是它本身的扩展。在C++标准中并无要求到这一点,它只规定catch(…)必须能捕获程序中全部经过throw语句抛出的异常。所以上面的这个 程序在linux系统下的运行结果也彻底是符合C++标准的。虽然你们也必须认可window2000系统下对C++异常处理模型的这种扩展确实是一个很 不错的完善,极大得提升了程序的安全性。

为何要用catch(…)这个东东?

程序员朋友们也许会说,这还有问吗?这篇文章的一开始不就讲到了吗?catch(…)可以捕 获多种数据类型的异常对象,因此它提供给程序员一种对异常 对象更好的控制手段,使开发的软件系统有很好的可靠性。所以一个比较有经验的程序员一般会这样组织编写它的代码模块,以下:

void Func()
{
try
{
// 这里的程序代码完成真正复杂的计算工做,这些代码在执行过程当中
// 有可能抛出DataType一、DataType2和DataType3类型的异常对象。
}
catch(DataType1& d1)
{
}
catch(DataType2& d2)
{
}
catch(DataType3& d3)
{
}
// 注意上面try block中可能抛出的DataType一、DataType2和DataType3三
// 种类型的异常对象在前面都已经有对应的catch block来处理。但为何
// 还要在最后再定义一个catch(…) block呢?这就是为了有更好的安全性和
// 可靠性,避免上面的try block抛出了其它未考虑到的异常对象时致使的程
// 序出现意外崩溃的严重后果,并且这在用VC开发的系统上更特别有效,因
// 为catch(…)能捕获系统出现的异常,而系统异常每每令程序员头痛了,现
// 在系统通常都比较复杂,并且由不少人共同开发,一不当心就会致使一个
// 指针变量指向了其它非法区域,结果意外灾难不幸发生了。catch(…)为这种
// 潜在的隐患提供了一种有效的补救措施。
catch(…)
{
}
}

还有,特别是VC程序员为了使开发的系统有更好的可靠性,每每在应用程序的入口函数中(如MFC框架的开发环境下 CXXXApp::InitInstance())和工做线程的入口函数中加上一个顶层的trycatch块,而且使用catch(…)来捕获一切全部的 异常,以下:

BOOL CXXXApp::InitInstance()
{
if (!AfxSocketInit())
{
AfxMessageBox(IDP_SOCKETS_INIT_FAILED);
return FALSE;
}

AfxEnableControlContainer();

// Standard initialization
// If you are not using these features and wish to reduce the size
// of your final executable, you should remove from the following
// the specific initialization routines you do not need.

#ifdef _AFXDLL
Enable3dControls(); // Call this when using MFC in a shared DLL
#else
Enable3dControlsStatic(); // Call this when linking to MFC statically
#endif


// 注意这里有一个顶层的trycatch块,而且使用catch(…)来捕获一切全部的异常
try
{
CXXXDlg dlg;
m_pMainWnd = &dlg;
int nResponse = dlg.DoModal();
if (nResponse == IDOK)
{
// TODO: Place code here to handle when the dialog is
// dismissed with OK
}
else if (nResponse == IDCANCEL)
{
// TODO: Place code here to handle when the dialog is
// dismissed with Cancel
}
}
catch(…)
{
// dump出系统的一些重要信息,并通知管理员查找出现意外异常的缘由。
// 同时想办法恢复系统,例如说从新启动应用程序等
}

// Since the dialog has been closed, return FALSE so that we exit the
// application, rather than start the application's message pump.
return FALSE;
}

   经过上面的例程和分析能够得出,因为catch(…)可以捕获全部数据类型的异常对象,因此在恰当的地方使用catch(…)确实可使软件系统有着更 好的可靠性。这确实是你们使用catch(…)这个东东最好的理由。但不要误会的是,在C++异常处理模型中,不仅有catch(…)方法可以捕获几乎所 有类型的异常对象(也许有其它更好的方法,在下一篇文章中主人公阿愚带你们一同去探讨一下),可C++标准中为何会想到定义这样一个catch(…) 呢?有过java或C#编程开发经验的程序员会发现,在它们的异常处理模型中,并无这样相似的一种语法,可这里不得再也不次强调的是,java中的异常处 理模型是C++中的异常处理模型的完善改进版,可它反而没有了catch(…),为什么呢?仍是先去看看下一章吧,“C++的异常处理和面向对象的紧密关系 ”。也许你们能找到一个彷佛合理的缘由。

相关文章
相关标签/搜索