一、c++
c++的异常处理机制包括:
抛出异常
捕获异常
处理异常
C++使用throw抛出异常,try…catch捕获和处理异常。
例如:
#include<stdio.h>
#include<string.h>
char* _strcpy(char *a, const char *s)
{
try
{
char *temp;
printf("before throw ");
if (NULL == a || NULL == s)
throw string("Invalid argument(s)");//抛出异常。这里还有疑问:究竟谁可以抛出异常?程序自己肯定可以, //但有的例子中并没有throw语句,难不成是系统抛出的?
temp = a;
printf("after throw");
return temp;
}
catch(string &e)//捕获并处理异常,格式为catch(异常类型名 异常对象名)
{
printf("%s\n", e.c_str());
} catch(int i) {
printf("%d\n", i);
}
}
int main(int argc, char* argv[])
{
char a[32];
_strcpy(a,NULL);
return0;
}
在执行完throw语句后,系统将不执行throw后面的语句,而是直接跳到相应的catch块执行相应的异常处理。执行完catch块后直接转到try - catch后面的语句执行。如上例,系统将输出“before throw Invalid argument(s)”。
try - catch语句可以嵌套。
c++中的异常规范提供了在函数生命中描述该函数可能抛出异常的方法,异常规范跟随在函数参数列表之后,它使用关键字throw来指定,举例如下:
int f(int a,int b);//f可能抛出任何异常
int g(int a,int b) throw(char,string);//g可能抛出整型、字符串型异常
int h(int a,int b) throw();//h不会抛出异常
二、windows
windows的异常处理机制,称为SEH( structured exception handling ), SEH的异常处理模型包括__try-__except语句和__try-__finally语句。
__try-__except异常处理机制和C++的很相像,只是关键字是__except而不是catch。另外catch关键字后面好像接受一个函数参数一样,可以是各种类型的异常数据对象,但是__except关键字则不同,它后面跟的是一个表达式(可以是各种类型的表达式)。
例如:
void main()
{
puts("hello");
// 定义受监控的代码模块
__try
{
puts("in try");
}
//定义异常处理模块
__except(EXCEPTION_EXECUTE_HANDLER)
{
puts("in except");
}
puts("world");
}
受监控的代码模块被执行(也即__try定义的模块代码);
如果上面的代码执行过程中,没有出现异常的话,那么控制流将转入到__except子句之后的代码模块中;
否则,如果出现异常的话,那么控制流将进入到__except后面的表达式中,也即首先计算这个表达式的值,之后再根据这个值,来决定做出相应的处理。
EXCEPTION_CONTINUE_EXECUTION (–1) 异常被忽略,控制流将在异常出现的点之后,继续恢复运行。
EXCEPTION_CONTINUE_SEARCH (0) 异常不被识别,也即当前的这个__except模块不是这个异常错误所对应的正确的异常处理模块。系统将继续到上一层的__try-__except域中继续查找一个恰当的__except模块。
EXCEPTION_EXECUTE_HANDLER (1) 异常已经被识别,也即当前的这个异常错误,系统已经找到了并能够确认,这个__except模块就是正确的异常处理模块。控制流将进入到__except模块中。
为了在__except模块中获得异常错误的相关信息,Windows提供了两个API函数
LPEXCEPTION_POINTERS GetExceptionInformation(VOID);
DWORD GetExceptionCode(VOID);
SEH异常处理模型中,对异常划分为两大类,第一种就是上面一些例程中所见到的,这类异常是系统异常,也被称为硬件异常;还有一类,就是程序中自己抛出异常,被称为软件异常(这和C++的throw关键字作用相似)。抛出软件异常需要调用Windows提供的API函数,它的声明如下:
VOID RaiseException(
DWORD dwExceptionCode, // exception code
DWORD dwExceptionFlags, // continuable exception flag
DWORD nNumberOfArguments, // number of arguments in array
CONST DWORD *lpArguments // address of array of arguments
);
__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)”。。
第3种情况,也即由于出现异常而导致的“全局展开”,对于程序员而言,这也许是无法避免的,因为你在利用异常处理机制提高程序可靠健壮性的同时,不可避免的会引起性能上其它的一些开销。呵呵!这世界其实也算瞒公平的,有得必有失。
有用的博文:
www.doc88.com/p-90394592801.html
http://www.cnblogs.com/wenziqi/archive/2010/08/26/1809074.html
http://www.cppblog.com/yehao/articles/165099.html