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