try finally

 __try

{

 ...1

return;

}

__finally

{

...2

}

 

...3

return;

 

当try语句执行到return,先执行finally,然后return,3处语句不会被执行。

 

int i=0;
    __try
    {
        return ++i;
    }
    __finally
    {
        cout << i;
    }

输入内容为 1;

 

DWORD FuncaDoodleDoo()
{
    DWORD dwTemp = 0;
    while(dwTemp < 10)
    {
        __try
        {
            if(dwTemp == 2)
            {
                continue;
            }

            if(dwTemp == 3)
            {
                break;
            }
        }
        __finally
        {
            dwTemp++;
        }
        dwTemp++;
    }
    dwTemp += 10;
    return (dwTemp);
}

 

执行顺序

首先d w Te m p被设置成0。t r y块中的代码执行,但两个
i f语句的值都不为T R U E。执行自然移到f i n a l l y块中的代码,在这里d w Te m p增加到1。然后
f i n a l l y块之后的指令又增加d w Te m p,使它的值成为2。
第23章结束处理程序计计569
下载
当循环继续,d w Te m p为2,t r y块中的c o n t i n u e语句将被执行。如果没有结束处理程序在从
t r y块中退出之前强制执行f i n a l l y块,执行就立即跳回w h i l e测试,d w Te m p不会被改变,将出现
无限(死)循环。利用一个结束处理程序,系统知道c o n t i n u e语句要引起控制流过早退出t r y块,
而将执行移到f i n a l l y块。在f i n a l l y块中,d w Te m p被增加到3。但f i n a l l y块之后的代码不执行,因
为控制流又返回到c o n t i n u e,再到循环的开头。
现在我们处理循环的第三次重复。这一次,第一个i f语句的值是FA L S E,但第二个语句的
值是T R U E。系统又能够捕捉要跳出t r y块的企图,并先执行f i n a l l y块中的代码。现在d w Te m p增
加到4。由于b r e a k语句被执行,循环之后程序部分的控制恢复。这样, f i n a l l y块之后的循环中
的代码没有执行。循环下面的代码对d w Te m p增加1 0,这时d w Te m p的值是1 4,这就是调用这个
函数的结果。当然,实际上我们不会写出F u n c a D o o d l e D o o这样的代码。这里将c o n t i n u e和b r e a k
语句放在代码的中间,是为了说明结束处理程序的操作

尽管结束处理程序可以捕捉t r y块过早退出的大多数情况,但当线程或进程被结束时,它不
能引起f i n a l l y块中的代码执行。当调用E x i t T h r e a d或E x i t P r o c e s s时,将立即结束线程或进程,而
不会执行f i n a l l y 块中的任何代码。另外,如果由于某个程序调用Te r m i n a t e T h r e a d或
Te r m i n a t e P r o c e s s,线程或进程将死掉, f i n a l l y块中的代码也不执行。某些C运行期函数(例如
a b o r t)要调用E x i t P r o c e s s,也使f i n a l l y块中的代码不能执行。虽然没有办法阻止其他程序结束
你的一个线程或进程,但你可以阻止你自己过早调用E x i t T h r e a d和E x i t P r o c e s s。

 

DWORD Funcenstein4()
{
    DWORD dwTemp = 0;

    __try
    {
        if(dwTemp == 1)

        {

            ....
            return dwTemp ;

        }
    }
    __finally
    {

        ....

        return 103;
    }

    dwTemp = 9;
    return (dwTemp);

}

在F u n c e n s t e i n 4中,t r y块将要执行,并试图将d w Te m p的值(5)返回给F u n c e n s t e i n 4的调用
者。如同对F u n c e n s t e i n 2的讨论中提到的,试图从t r y块中过早的返回将导致产生代码,把返回
570计计第五部分结构化异常处理
下载
值置于由编译程序建立的临时变量中。然后, f i n a l l y块中的代码被执行。在这里,与
F u n c e n s t e i n 2不同的是在f i n a l l y块中增加了一个r e t u r n语句。F u n c e n s t e i n 4会向调用程序返回5还
是1 0 3?这里的答案是1 0 3,因f i n a l l y块中的r e t u r n语句引起值1 0 3存储在值5所存储的临时变量
中,覆盖了值5。当f i n a l l y块完成执行,现在临时变量中的值( 1 0 3)从F u n c e n s t e i n 4返回给调用
程序。
我们已经看到结束处理程序在补救t r y块中的过早退出的执行方面很有效,但也看到结束处
理程序由于要阻止t r y块的过早退出而产生了我们不希望有的结果。更好的办法是在结束处理程
序的t r y块中避免任何会引起过早退出的语句。实际上,最好将r e t u r n、c o n t i n u e、b r e a k、g o t o
等语句从结束处理程序的t r y块和f i n a l l y块中移出去,放在结束处理程序的外面。这样做会使编
译程序产生较小的代码,因为不需要再捕捉t r y块中的过早退出,也使编译程序产生更快的代码
(因为执行局部展开的指令也少)。另外,代码也更容易阅读和维护。

DWORD Funcarama4()
{
    HANDLE hFile = INVALID_HANDLE_VALUE;
    PVOID pvBuf = NULL;
    BOOL fFunctionOk = FALSE;
    __try
    {
        DWORD dwNumBytesRead;
        BOOL fOk;
        hFile = CreateFile("test.txt", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
        if(hFile == INVALID_HANDLE_VALUE)
        {
            __leave;
        }

        pvBuf = VirtualAlloc(NULL, 1024, MEM_COMMIT, PAGE_READWRITE);
        if(pvBuf == NULL)
        {
            __leave;
        }

        fOk = ReadFile(hFile, pvBuf, 1024, &dwNumBytesRead, NULL);
        if(!fOk || (dwNumBytesRead == 0))
        {
            __leave;
        }
        fFunctionOk = TRUE;
    }
    __finally
    {
        if(pvBuf != NULL)
        {
            VirtualFree(pvBuf, MEM_RELEASE | MEM_DECOMMIT);
        }

        if(hFile != INVALID_HANDLE_VALUE)
        {
            CloseHandle(hFile);
        }
    }

    return fFunctionOk;
}

在t r y块中使用- - l e a v e关键字会引起跳转到t r y块的结尾。可以认为是跳转到t r y块的右大括
号。由于控制流自然地从t r y块中退出并进入f i n a l l y块,所以不产生系统开销。当然,需要引入
一个新的B o o l e a n型变量f F u n c t i o n O k,用来指示函数是成功或失败。这是比较小的代价。
当按照这种方式利用结束处理程序来设计函数时,要记住在进入t r y块之前,要将所有资源
句柄初始化为无效的值。然后,在f i n a l l y块中,查看哪些资源被成功的分配,就可以知道哪些
要释放。另外一种确定需要释放资源的办法是对成功分配的资源设置一个标志。然后, f i n a l l y
块中的代码可以检查标志的状态,来确定资源是否要释放。

 

如果控制流非正常退出t r y块—通常由于g o t o、r e t u r n、b r e a k或c o n t i n u e语句引起
的局部展开,或由于内存访问违规或其他异常引起的全局展开—对A b n o r m a l Te r m i n a t i o n的调
用将返回T R U E。没有办法区别f i n a l l y块的执行是由于全局展开还是由于局部展开。但这通常
不会成为问题,因为可以避免编写执行局部展开的代码。

 

try finally 优点

简化错误处理,因所有的清理工作都在一个位置并且保证被执行。
• 提高程序的可读性。
• 使代码更容易维护。
• 如果使用得当,具有最小的系统开销。

你可能感兴趣的:(try finally)