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

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

  1. try
  2. {
  3. BYTE* pch ;
  4. pch = ( BYTE* )00001234 ;   //给予一个非法地址
  5. *pch = 6 ; //对非法地址赋值,会造成Access Violation 异常
  6. }
  7. catch(...)
  8. {
  9. AfxMessageBox( "catched" ) ;
  10. }

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

那么能否在release代码优化状态下捕获这个异常呢, 答案是有的。 就是__try, __except结构, 上述代码如果改成如下代码异常即可捕获。

  1. __try
  2. {
  3. BYTE* pch ;
  4. pch = ( BYTE* )00001234 ;   //给予一个非法地址
  5. *pch = 6 ; //对非法地址赋值,会造成Access Violation 异常
  6. }
  7. __except( EXCEPTION_EXECUTE_HANDLER )
  8. {
  9. AfxMessageBox( "catched" ) ;
  10. }

但是用__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.

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

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

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

  2、哈哈!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; } }

   3、同样,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; } }

  4、对于抽象数据类型的异常对象。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(…)有点迷糊? 1、究竟对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 { // 这里的程序代码完成真正复杂的计算工作,这些代码在执行过程中 // 有可能抛出DataType1、DataType2和DataType3类型的异常对象。 } catch(DataType1& d1) { } catch(DataType2& d2) { } catch(DataType3& d3) { } // 注意上面try block中可能抛出的DataType1、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++的异常处理和面向对象的紧密关系 ”。也许大家能找到一个似乎合理的原因。

c++ try catch 问题的更多相关文章

  1. SQLServer如何添加try catch

    在.net中我们经常用到try catch.不过在sqlserver中我们也可以使用try catch捕捉错误,在这里把语法记录下来和大家分享一下, --构建存储过程CREATE PROCEDURE ...

  2. try...catch..finally

    try..catch..finally try{ 代码块1 }catch(Exception e){ 代码块2 }finally{ 代码块3 } catch是抓取代码块1中的异常 代码块2是出异常后的 ...

  3. C++异常处理:try,catch,throw,finally的用法

    写在前面 所谓异常处理,即让一个程序运行时遇到自己无法处理的错误时抛出一个异常,希望调用者可以发现处理问题. 异常处理的基本思想是简化程序的错误代码,为程序键壮性提供一个标准检测机制. 也许我们已经使 ...

  4. POJ 3278 Catch That Cow(bfs)

    传送门 Catch That Cow Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 80273   Accepted: 25 ...

  5. [c#基础]关于try...catch最常见的笔试题

    引言 在翻看之前总结的常见面试题中,关于try...catch异常处理的还是蛮多了,今天看到这个面试题,也就重新学习一下. try..catch语法 try-catch语句由一个try块后跟一个或多个 ...

  6. 高程(4):执行环境、作用域、上下文执行过程、垃圾收集、try...catch...

    高程三 4.2.4.3 一.执行环境 1.全局执行环境是最外层的执行环境. 2.每个函数都有自己的执行环境,执行函数时,函数环境就会被推入一个当前环境栈中,执行完毕,栈将其环境弹出,把控制器返回给之前 ...

  7. try catch里面try catch嵌套

    try catch里能否内嵌try catch?答案是肯定的.但是等内层try catch出异常之后是个什么执行顺序呢?看下面代码 static void Main(string[] args) { ...

  8. 基础知识《十》java 异常捕捉 ( try catch finally ) 你真的掌握了吗?

    本文转载自  java 异常捕捉 ( try catch finally ) 你真的掌握了吗? 前言:java 中的异常处理机制你真的理解了吗?掌握了吗?catch 体里遇到 return 是怎么处理 ...

  9. java try(){}catch(){}自动资源释放

    从 Java 7 build 105 版本开始,Java 7 的编译器和运行环境支持新的 try-with-resources 语句,称为 ARM 块(Automatic Resource Manag ...

  10. Java throws Exception、try、catch

    throws Exception是方法后面接的 意思是向上级抛出异常 try{}里面的异常会被外面的catch捕捉到 抛出异常是throw new Exception("异常"); ...

随机推荐

  1. SKlearn库学习曲线

    思想: # 1.现将所有样本用交叉验证方法或者(随机抽样方法) 得到 K对 训练集-验证集# 2.依次对K个训练集,拿出数量不断增加的子集如m个,并在这些K*m个子集上训练模型.# 3.依次在对应训练 ...

  2. LR提交JSON格式的请求

    https://www.cnblogs.com/yangxia-test/p/5584317.html

  3. notepad++ 快捷键大全、notepad常用快捷键

    Notepad++ 快捷键 大全, notepad++也情有独钟,最近发现了一个快捷键,就是选中单词,ctrl+shift+enter.不过现在想知道一个快捷键,假设有三行代码,选中后一般按TAB就可 ...

  4. Linux VMware tools安装步骤

    Linux VMware tools安装步骤: 1.安装环境介绍 #虚拟机版本:VMware-workstation-full-10 #linux分发版本:CentOS-6.4-i386-LiveCD ...

  5. [转]CentOS7 下安装svn

    1. 安装 centos(我这里使用的是CentOS7)下yum命令即可方便的完成安装 $ sudo yum install subversion 测试安装是否成功: $ svnserve --ver ...

  6. CCF2016093炉石传说(C语言版)

    问题描述 <炉石传说:魔兽英雄传>(Hearthstone: Heroes of Warcraft,简称炉石传说)是暴雪娱乐开发的一款集换式卡牌游戏(如下图所示).游戏在一个战斗棋盘上进行 ...

  7. laravel 列表搜索查询(when,with用法以及关联图像id处理图像路径)

    laravel中比较常规的列表查询: /** * 活动列表 * @param Request $request * @return \Illuminate\Http\JsonResponse */ p ...

  8. cf 1041C双指针

    比赛的时候想着用单调队列做... 打完发现其实就是个双指针 /* 构造双指针解决即可 */ #include<iostream> #include<cstring> #incl ...

  9. Linux学习笔记:scp远程拷贝文件

    scp是secure copy的简写,用于Linux下进行远程拷贝文件的命令,类似的有cp,不过cp仅在本机上进行拷贝不能跨服务器. 命令格式: scp [参数] [原路径] [目标路径] -q 不显 ...

  10. python全栈开发day21-2 几个装饰器总结

    1 @property 将一个方法伪装成属性 2.@propertty @f.setter 设置伪装成方法的属性 3.@propertty @f.deleter 删除一个伪装成方法的属性. class ...