SEH与C异常模型的混合使用.docx
- 文档编号:9477526
- 上传时间:2023-02-04
- 格式:DOCX
- 页数:16
- 大小:19.66KB
SEH与C异常模型的混合使用.docx
《SEH与C异常模型的混合使用.docx》由会员分享,可在线阅读,更多相关《SEH与C异常模型的混合使用.docx(16页珍藏版)》请在冰豆网上搜索。
SEH与C异常模型的混合使用
SEH与C++异常模型的混合使用
SEH与C++异常模型的混合使用
朋友们,准备好了心情吗?
这可是有点复杂呦!
如何混合使用呢?
同样,还是看例子先。
仍然是在原来例程的代码基础上做修改,修改后的代码如下:
//注意,这是C++程序,文件名为:
SEH-test.cpp
#include"stdio.h"
classA
{
public:
voidf1(){}
//抛出C++异常
voidf2(){throw888;}
};
//这个函数中使用了try-catch处理异常,也即C++异常处理
voidtest1()
{
Aa1;
Aa2,a3;
try
{
a2.f1();
a3.f2();
}
catch(interrorcode)
{
printf("catchexception,errorcode:
%d\n",errorcode);
}
}
//这个函数没什么改变,仍然采用try-except异常机制,也即SEH机制
voidtest()
{
int*p=0x00000000;//pointertoNULL
__try
{
//这里调用test1函数
test1();
puts("intry");
__try
{
puts("intry");
//causesanaccessviolationexception;
//导致一个存储异常
*p=13;
puts("这里不会被执行到");
}
__finally
{
puts("infinally");
}
puts("这里也不会被执行到");
}
__except(puts("infilter1"),0)
{
puts("inexcept1");
}
}
voidmain()
{
puts("hello");
__try
{
test();
}
__except(puts("infilter2"),1)
{
puts("inexcept2");
}
puts("world");
}
上面程序不仅能够被编译通过,而且运行结果也是正确的(和预期的一样,同样符合C++异常处理模型的规则,和SEH异常模型的处理规则)。
其结果如下:
hello
catchexception,errorcode:
888
intry
intry
infilter1
infilter2
infinally
inexcept2
world
Pressanykeytocontinue
继续深入刚才的例子
上面的例程中,虽然在同一个程序中既有try-catch机制,又有try-except机制,当然这也完全算得上SEH与C++异常模型的混合使用。
但是,请注意,这两种机制其实是完全被分割开的,它们完全被分割在了两个函数的内部。
也即这两种机制其实并没有完全交互起来,换句话说,它们还算不上两种异常处理机制真正的混合使用。
这里继续给出一个更绝的例子,还是先来看看代码吧,如下:
//注意,这是C++程序,文件名为:
SEH-test.cpp
#include"stdio.h"
classMyException
{
public:
MyException(){printf("构造一个MyException对象\n");}
MyException(constMyException&e){printf("复制一个MyException对象\n");}
operator=(constMyException&e){printf("复制一个MyException对象\n");}
~MyException(){printf("析构一个MyException对象\n");}
};
classA
{
public:
A(){printf("构造一个A对象\n");}
~A(){printf("析构一个A对象\n");}
voidf1(){}
//注意,这里抛出了一个MyException类型的异常对象
voidf2(){MyExceptione;throwe;}
};
//这个函数中使用了try-catch处理异常,也即C++异常处理
voidtest1()
{
Aa1;
Aa2,a3;
try
{
a2.f1();
a3.f2();
}
//这里虽然有catch块,但是它捕获不到上面抛出的C++异常对象
catch(interrorcode)
{
printf("catchexception,errorcode:
%d\n",errorcode);
}
}
//这个函数没什么改变,仍然采用try-except异常机制,也即SEH机制
voidtest()
{
int*p=0x00000000;//pointertoNULL
__try
{
//这里调用test1函数
//注意,test1函数中会抛出一个C++异常对象
test1();
puts("intry");
__try
{
puts("intry");
*p=13;
puts("这里不会被执行到");
}
__finally
{
puts("infinally");
}
puts("这里也不会被执行到");
}
__except(puts("infilter1"),0)
{
puts("inexcept1");
}
}
voidmain()
{
puts("hello");
__try
{
test();
}
//这里能捕获到C++异常对象吗?
拭目以待吧!
__except(puts("infilter2"),1)
{
puts("inexcept2");
}
puts("world");
}
仔细阅读上面的程序,不难看出,SEH与C++异常模型两种机制确实真正地交互起来了,上层的main()函数和test()函数采用try-except语句处理异常,而下层的test1()函数采用标准的try-catch语句处理异常,并且,下层的test1()函数所抛出的C++异常会被上层的try-except所捕获到吗?
还是看运行结果吧!
如下:
hello
构造一个A对象
构造一个A对象
构造一个A对象
构造一个MyException对象
复制一个MyException对象
infilter1
infilter2
析构一个MyException对象
析构一个A对象
析构一个A对象
析构一个A对象
inexcept2
world
Pressanykeytocontinue
结果是否和朋友们的预期一致呢?
它的的确确是上层的try-except块,能够捕获到下层的test1()函数所抛出的C++异常,而且流程还是正确的,即符合了SEH异常模型的规则,又同时遵循了C++异常模型的规则。
同时,最难能可贵的是,在test1()函数中的三个局部变量,都被正确的析构了(这非常神奇吧!
具体的机制这里暂且不详细论述了,在后面阐述“异常机制的实现”的文章中再做论述)。
细心的程序员朋友们,也许从上面程序的运行结果中发现了一些“问题”,什么呢?
那就是“MyException对象”构造了两次,但它只被析构了一次。
呵呵!
这也许就是MSDN中不建议混合使用这两种异常处理机制的背后原因之一吧!
虽然说,这种问题不至于对整个程序造成很大的破坏性影响,但主人公阿愚却坚持认为,如果我们编程时滥用try-except和try-catch在一起混用,不仅使我们程序的整体结构和语义受到影响,而且也会造成一定的内存资源泄漏,甚至其它的不稳定因素。
总之,在C++程序中运用try-except机制,只有在顶层的函数作用域中(例如,系统运行库中,或plugin的钩子中)才有必要这样做。
如在VC编写的程序中,每当我们程序运行中出现意外异常导致的崩溃事件时,系统总能够弹出一个“应用程序错误”框,如下:
NT操作系统是如何实现的呢?
很简单,它就是在在VC运行库中的顶层的函数内采用了try-except机制,不信,看看如下截图代码吧!
C++异常处理模型能捕获SEH异常吗?
呵呵!
阿愚笑了,这还用问吗?
当然了,VC提供的C++异常处理模型的强大之处就是,它不仅能捕获C++类型的异常,而且它还能捕获属于系统级别的SEH异常。
它就是利用了catch(…)语法,在前面专门阐述catch(…)语法时,我们也着重论述了这一点。
不过,这里还是给出一个实际的例子吧,代码如下:
classMyException
{
public:
MyException(){printf("构造一个MyException对象\n");}
MyException(constMyException&e){printf("复制一个MyException对象\n");}
operator=(constMyException&e){printf("复制一个MyException对象\n");}
~MyException(){printf("析构一个MyException对象\n");}
};
classA
{
public:
A(){printf("构造一个A对象\n");}
~A(){printf("析构一个A对象\n");}
voidf1(){}
//抛出C++异常
voidf2(){MyExceptione;throwe;}
};
voidtest()
{
int*p=0x00000000;//pointertoNULL
__try
{
puts("intry");
__try
{
puts("intry");
//causesanaccessviolationexception;
//导致一个存储异常
*p=13;
//呵呵,注意这条语句
puts("这里不会被执行到");
}
__finally
{
puts("infinally");
}
//呵呵,注意这条语句
puts("这里也不会被执行到");
}
__except(puts("infilter1"),0)
{
puts("inexcept1");
}
}
voidtest1()
{
Aa1;
Aa2,a3;
try
{
//这里会产生一个SEH类型的系统异常
test();
a2.f1();
a3.f2();
}
//捕获得到吗?
catch(...)
{
printf("catchunknownexception\n");
}
}
voidmain()
{
puts("hello");
__try
{
test1();
}
__except(puts("infilter2"),0)
{
puts("inexcept2");
}
puts("world");
}
上面的程序很简单的,无须进一步讨论了。
当然,其实我们还可以更进一步深入进去,因为C++异常处理模型不仅能够正常捕获到SEH类型的系统异常,而且它还能够把SEH类型的系统异常转化为C++类型的异常。
我想,这应该放在单独的一篇文章中来阐述了,其实这在许多关于Window系统编程的书籍中也有详细讨论。
SEH与C++异常模型在混合使用时的“禁区”
刚才我们看到,利用try-except来捕获C++异常有点小问题,但这毕竟算不上什么禁区。
那么,何为SEH与C++异常模型在混合使用时的“禁区”呢?
看个例子吧,代码如下:
//注意,这是C++程序,文件名为:
SEH-test.cpp
#include"stdio.h"
voidmain()
{
int*p=0x00000000;//pointertoNULL
//这里是SEH的异常处理语法
__try
{
puts("intry");
//这里是C++的异常处理语法
try
{
puts("intry");
//causesanaccessviolationexception;
//导致一个存储异常
*p=13;
//呵呵,注意这条语句
puts("这里不会被执行到");
}
catch(...)
{
puts("catchanything");
}
//呵呵,注意这条语句
puts("这里也不会被执行到");
}
__except(puts("infilter1"),1)
{
puts("inexcept1");
}
}
朋友们!
不要急于编译并测试上面的小程序,先猜猜它会有什么结果呢?
想到了吗?
不妨实践一下,呵呵!
实际结果是否令你吃惊呢?
对了,没错,VC就是会报出一个编译错误(“errorC2713:
Onlyoneformofexceptionhandlingpermittedperfunction”)。
那么原因何在呢?
主人公阿愚在此一定“知无不言,言无不尽”,这是因为:
VC实现的异常处理机制,不管是try-except模型,还是try-catch模型,它们都是以函数作为一个最基本“分析和控制”的目标,也即,如果一个函数内使用了异常处理机制,VC编译器在编译该函数时,它会给此函数插入一些“代码和信息”(代码指的是当该函数中出现异常时的回调函数,而信息主要是指与异常出现相关的一些必要的链表),因此每份函数只能有一份这样的东东(“代码和信息”),故一个函数只能采用一种形式的异常处理规则。
朋友们!
恍然大悟了吧!
其实这倒不算最令人不可思议的。
还有一种更为特殊的情况,看下面的例子,代码如下:
classA
{
public:
A(){printf("构造一个A对象\n");}
~A(){printf("析构一个A对象\n");}
voidf1(){}
voidf2(){}
};
voidmain()
{
__try
{
Aa1,a2;
puts("intry");
}
__except(puts("infilter1"),1)
{
puts("inexcept1");
}
}
其实上面的程序表面上看,好像是没有什么特别的吗?
朋友们!
仔细看看,真的没什么特别的吗?
不妨编译一下该程序,又奇怪了吧!
是的,它同样也编译报错了,这是我机器上编译时产生的信息,如下:
--------------------Configuration:
exception-Win32Debug--------------------
Compiling...
seh-test.cpp
f:
\exception\seh-test.cpp(214):
warningC4509:
nonstandardextensionused:
'main'usesSEHand'a2'hasdestructor
f:
\exception\seh-test.cpp(211):
seedeclarationof'a2'
f:
\exception\seh-test.cpp(214):
warningC4509:
nonstandardextensionused:
'main'usesSEHand'a1'hasdestructor
f:
\exception\seh-test.cpp(211):
seedeclarationof'a1'
f:
\exception\seh-test.cpp(219):
errorC2712:
Cannotuse__tryinfunctionsthatrequireobjectunwinding
Errorexecutingcl.exe.
Creatingbrowseinfofile...
exception.exe-1error(s),2warning(s)
那么,上面的错误信息代表什么意思,我想是不是有不少朋友都遇到过这种莫名奇妙的编译问题。
其实,这确实很令人费解,明明程序很简单的吗?
而且程序中只用到了SEH异常模型的try-except语法,甚至SEH与C++异常模型两者混合使用的情况都不存在。
那么编译出错的原因究竟何在呢?
实话告诉你吧!
其实主人公阿愚在此问题上也是费透了脑筋,经过了一番深入而细致的钻研之后,才知道真正原因的。
那原因就是:
同样还是由于在一个函数不能采用两种形式的异常处理机制而导致的编译错误。
啊!
这岂不是更迷惑了。
其实不然,说穿了,朋友们就会明白了,这是因为:
在C++异常处理模型中,为了能够在异常发生后,保证正确地释放相关的局部变量(也即调用析构函数),它必须要跟踪每一个“对象”的创建过程,这种由于异常产生而导致的对象析构的过程,被称为“unwind”(记得前面的内容中,也多次讲述到了这个名词),因此,如果一个函数中有局部对象的存在,那么它就一定会存在C++的异常处理机制(也即会给此函数插入一些用于C++异常处理“代码和信息”),这样,如果该函数中在再使用try-except机制,岂不是就冲突了吗?
所以编译器也就报错了,因为它处理不了了!
哈哈!
朋友们,主人公阿愚把问题说清楚了吗?
总结
SEH与C++异常模型,可以在一起被混合使用。
但最好听从MSDN的建议:
在C程序中使用try-except和try-finally;而C++程序则应该使用try-catch。
混合使用时,C++异常模型可以捕获SEH异常;而SEH异常模型也可以捕获C++类型的异常。
而后者通常有点小问题,它一般主要运用在提高和保证产品的可靠性上(也即在顶层函数中使用try-except语句来catch任何异常)
VC实现的异常处理机制中,不管是try-except模型,还是try-catch模型,它们都是以函数作为一个最基本“分析和控制”的目标,也即一个函数中只能采用一种形式的异常处理规则。
否则,编译这一关就会被“卡壳”。
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- SEH与C 异常模型的混合使用 SEH 异常 模型 混合 使用