注意
:
不要将结构化异常处理同
C++
的异常处理相混淆
.C++
异常处理是一种不同形式的异常处理
,
其形式是使用
C++
关键字
catch
和
throw.
微软的
Visual
C++
也支持
C++
的异常处理
,
并且在内部实现上利用了已经引入到编译程序和
Windows
操作系统的结构化异常处理的功能
.
SEH
实际包含两个主要功能
:
结束处理
(termination
handling)
和异常处理
(
exception
handling).
__try
和
__finally
关键字用来标出结束处理程序两段代码的轮廓
.
在上面的代码段中
,
操作系统和编译程序共同来确保结束处理程序中的
__finally
代码块能够被执行
,
不管保护体
(try
块
)
是如何退出的
.
不论你在保护体中使用
return,
还是
goto,
或者是
longjump,
结束处理程序
(finally
块
)
都将被调用
.
当编译程序看到
try
块中的
goto
语句
,
它首先生成一个局部展开来执行
finally
块中的内容
.
注意
:
当控制流自然地离开
try
块并进入
finally
块时
,
进入
finally
块的系统开销是最小的
.
在
x86
CPU
上使用微软的编译程序
,
当执行离开
try
块进入
finally
块时
,
只有一个机器指令被执行
,
读者可以在自己的程序中注意到这种系统开销
.
当编译程序要生成额外的代码
,
系统要执行额外的工作时
,
系统开销就很值得注意了
.
尽管结束处理程序可以捕捉
try
块过早退出的大多数情况
,
但当线程或进程被结束时
,
它不能引起
finally
块中的代码执行
.
当调用
ExitThread
或
ExitProcess
时
,
将立即结束线程或进程
,
而不会执行
finally
块中的任何代码
.
另外
,
如果由于某个程序调用
TerminateThread
或
TerminateProcess,
线程或进程将死掉
,finally
块中的代码也不执行
.
某些
C
运行期函数
(
例如
abort)
要调用
ExitProcess,
也使
finally
块中的代码不能执行
.
虽然没有办法阻止其他程序结束你的一个线程或进程
,
但你可以阻止你自己过早调用
ExitThread
和
ExitProcess.
实际上
,
最好将
return
、
continue
、
break
、
goto
等语句从结束处理程序的
try
块和
finally
块中移出去
,
放在结束处理程序的外面
.
这样做会使编译程序产生较小的代码
,
因为不需要再捕捉
try
块中的过早退出
,
也使编译程序产生更快的代码
(
因为执行局部展开的指令也少
).
另外
,
代码也更容易阅读和维护
.
为了帮助我们尽可能避免写出让
try
块提前退出的代码,
Microsoft
为它的
C/C++
编译器加入一个关键字
:__leave,
会导致代码执行控制流跳转到
try
块的结尾,
这是正常进入
finally
语句,
开销最小
.
BOOL
Fun1()
{
BOOL
bRet
=
FALSE;
__try
{
…//
if(NULL
==
pBuff)
__leave;
}
__finally
{
…
}
r
eturn
bRet;
}
强制执行
finally
块的三种情况
:
1)
正常控制流
:
从
try
块到
finally
的正常代码控制流
;
2)
局部展开
:
从
try
块中的提前退出
(
由
goto,
longjump,
continue,
break,
return
等语句引起
)
将控制流强制转入
finally
块
;
3)
全局展开
.
由于以上三种情况中某一种的结果而导致
finally
块中的代码开始执行
.
为了确定是哪一种情况引起
finally
块执行
,
可以调用内部函数
AbnormalTermination.
这个内部函数只在
finally
块中调用
,
返回一个
Boolean
值
.
指出与
finally
块相结合的
try
块是否过早退出
.
换句话说
,
如果控制流离开
try
块并自然进入
finally
块
,AbnormalTermination
将返回
FALSE.
如果控制流非正常退出
try
块—通常由于
goto
、
return
、
break
或
continue
语句引起的局部展开
,
或由于内存访问违规或其他异常引起的全局展开—对
AbnormalTermination
的调用将返回
TRUE.
DWORD
Fun2()
{
__try
{
…
}
__finally
{
i
f(!AbnormalTermination())
{
…//
正常控制流
}
e
lse
{
...//
局部展开
}
}
r
eturn
0;
}