C++及Windows异常处理

C++及Windows异常处理(try,catch; __try,__finally; __try, __except)——一道笔试题引起的探究 - [笔试面试]

转自: http://shijuanfeng.blogbus.com/logs/178616871.html

题目:

    int*   p   =   0x00000000;       //   pointer   to   NULL 
    puts( "hello "); 
    __try{ 
        puts( "in   try "); 
        __try{ 
            puts( "in   try "); 
            *p   =   13;         //   causes   an   access   violation   exception; 
        }__finally{ 
            puts( "in   finally "); 
        } 
    }__except(puts( "in   filter "),   1){ 
        puts( "in   except "); 
    } 
    puts( "world "); 
    /*
    hello
    in try
    in try
    in filter
    in finally
    in except
    world
    */

上面的题目,我把答案列了出来。

常用C++的朋友,应该没见过__try这种形式的语句,下面我把try,catch; __try,__finally; __try, __except这三对异常处理使用标示逐一说明

 

本文参考了如下博文:

http://www.cnblogs.com/wenziqi/archive/2010/08/26/1809074.html

http://blog.csdn.net/lvwenshuai/article/details/6163342

http://topic.csdn.net/t/20030527/10/1838724.html

http://zhidao.baidu.com/question/183400727.html

 

  • C++ 异常处理:try,catch
try
{
    // 可能出错的语句
    // 如果有错,就——
    throw ... // 初始化一个异常对象(exception object)
 }
catch( 类型名 [形参名] ) /* 异常说明符(exception specifier)*/
 { 
    
 }
    
catch( 类型名 [形参名] )
 { 
    
}

C++的异常处理很简单,就是如上的三个关键字,注意C++中throw,catch之后没有Java等语言中的finally。

Q: 为何C++不提供“finally”结构?
A: 因为C++提供了另一种机制,完全可以取代finally,而且这种机制几乎总要比finally工作得更好:就是——“分配资源即初始化”。(见《The C++ Programming Language》14.4节)基本的想法是,用一个局部对象来封装一个资源,这样一来局部对象的析构函数就可以自动释放资源。这样,程序员就不会“忘记释放资源”了。 [译注:因为C++的对象“生命周期”机制替他记住了 :O) ] 下面是一个例子:

class File_handle {
        FILE* p;
    public:
        File_handle(const char* n, const char* a)
            { p = fopen(n,a); if (p==0) throw Open_error(errno); }
        File_handle(FILE* pp)
            { p = pp; if (p==0) throw Open_error(errno); }
 
        ~File_handle() { fclose(p); }
 
        operator FILE*() { return p; }
 
        // ...
    };
 
    void f(const char* fn)
    {
        File_handle f(fn,"rw");    // open fn for reading and writing
        // use file through f
    }

在一个系统中,每一样资源都需要一个“资源局柄”对象,但我们不必为每一个资源都写一个“finally”语句。在实作的系统中,资源的获取和释放的次数远远多于资源的种类,所以“资源分配即初始化”机制产生的代码要比“finally”机制少。

 

好了,接下来,看另外两组异常模型机制,它们是Windows系列操作系统平台上提供的SEH模型,也就是说在C++中调用的时候,其实是调用Windows的API

SEH,又称结构化异常处理.是设计Windows操作系统时提出一个种处理异常的方法。

  • __try, __except

这组异常处理机制和C++的很相像,只是关键字是except而不是catch

catch 和 except 的一点不同: catch关键字后面往往好像接受一个函数参数一样,可以是各种类型的异常数据对象;但是__except关键字则不同,它后面跟的却是一个表达式(可以是各种类型的表达式)

下面是一个例子:

void main()
{
    puts("hello");
    // 定义受监控的代码模块
    __try
    {
        puts("in try");
    }
    //定义异常处理模块
    __except(1)
    {
        puts("in except");
    }
    puts("world");
}

1. 受监控的代码模块被执行(也即__try定义的模块代码);
2. 如果上面的代码执行过程中,没有出现异常的话,那么控制流将转入到__except子句之后的代码模块中;
3. 否则,如果出现异常的话,那么控制流将进入到__except后面的表达式中,也即首先计算这个表达式的值,之后再根据这个值,来决定做出相应的处理。

EXCEPTION_CONTINUE_EXECUTION (–1) 异常被忽略,控制流将在异常出现的点之后,继续恢复运行。
EXCEPTION_CONTINUE_SEARCH (0) 异常不被识别,也即当前的这个__except模块不是这个异常错误所对应的正确的异常处理模块。系统将继续到上一层的try-except域中继续查找一个恰当的__except模块。
EXCEPTION_EXECUTE_HANDLER (1) 异常已经被识别,也即当前的这个异常错误,系统已经找到了并能够确认,这个__except模块就是正确的异常处理模块。控制流将进入到__except模块中。

小结:

(1) C++异常模型用try-catch语法定义,而SEH异常模型则用try-except语法;

(2) 与C++异常模型相似,try-except也支持多层的try-except嵌套。

(3) 与C++异常模型不同的是,try-except模型中,一个try块只能是有一个except块;而C++异常模型中,一个try块可以有多个catch块。

(4) 与C++异常模型相似,try-except模型中,查找搜索异常模块的规则也是逐级向上进行的。但是稍有区别的是,C++异常模型是按照异常对象的类型来进行匹配查找的;而try-except模型则不同,它通过一个表达式的值来进行判断。如果表达式的值为1(EXCEPTION_EXECUTE_HANDLER),表示找到了异常处理模块;如果值为0(EXCEPTION_CONTINUE_SEARCH),表示继续向上一层的try-except域中继续查找其它可能匹配的异常处理模块;如果值为-1(EXCEPTION_CONTINUE_EXECUTION),表示忽略这个异常,注意这个值一般很少用,因为它很容易导致程序难以预测的结果,例如,死循环,甚至导致程序的崩溃等。

(5) __except关键字后面跟的表达式,它可以是各种类型的表达式,例如,它可以是一个函数调用,或是一个条件表达式,或是一个逗号表达式,或干脆就是一个整型常量等等。最常用的是一个函数表达式,并且通过利用GetExceptionCode()或GetExceptionInformation ()函数来获取当前的异常错误信息,便于程序员有效控制异常错误的分类处理。

(6) SEH异常处理模型中,异常被划分为两大类:系统异常和软件异常。其中软件异常通过RaiseException()函数抛出。RaiseException()函数的作用类似于C++异常模型中的throw语句。

详细的请参看:http://www.cnblogs.com/wenziqi/archive/2010/08/26/1809074.html

 

  • __try, __finally

try-finally语句的语法与try-except很类似,稍有不同的是,__finally后面没有一个表达式,这是因为try- finally语句的作用不是用于异常处理,所以它不需要一个表达式来判断当前异常错误的种类。另外,与try-except语句类似,try- finally也可以是多层嵌套的,并且一个函数内可以有多个try-finally语句,不管它是嵌套的,或是平行的。当然,try-finally多层嵌套也可以是跨函数的。

最关键的一点: “不管在何种情况下,在离开当前的作用域时,finally块区域内的代码都将会被执行到”

void tmain()
{
    puts("hello");
    __try
    {
        puts("__try块中");
 
        // 注意,下面return语句直接让函数返回了
        return;
    }
    __finally
    {
        puts("__finally块中");
    }
 
    puts("world");

上面的程序运行结果如下:
hello
__try块中
__finally块中
Press any key to continue

 

小结:

__finally块被执行的流程时,无外乎三种情况。

第一种就是顺序执行到__finally块区域内的代码,这种情况很简单,容易理解;

第二种就是goto语句或return语句引发的程序控制流离开当前__try块作用域时,系统自动完成对__finally块代码的调用;

第三种就是由于在__try块中出现异常时,导致程序控制流离开当前__try块作用域,这种情况下也是由系统自动完成对__finally块的调用。

无论是第 2种,还是第3种情况,毫无疑问,它们都会引起很大的系统开销,编译器在编译此类程序代码时,它会为这两种情况准备很多的额外代码。

一般第2种情况,被称为“局部展开(LocalUnwinding)”;第3种情况,被称为“全局展开(GlobalUnwinding)”。在后面阐述SEH实现的时候会详细分析到这一点。

第3种情况,也即由于出现异常而导致的“全局展开”,对于程序员而言,这也许是无法避免的,因为你在利用异常处理机制提高程序可靠健壮性的同时,不可避免的会引起性能上其它的一些开销。呵呵!这世界其实也算瞒公平的,有得必有失。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值