灯火互联
管理员
管理员
  • 注册日期2011-07-27
  • 发帖数41778
  • QQ
  • 火币41290枚
  • 粉丝1086
  • 关注100
  • 终身成就奖
  • 最爱沙发
  • 忠实会员
  • 灌水天才奖
  • 贴图大师奖
  • 原创先锋奖
  • 特殊贡献奖
  • 宣传大使奖
  • 优秀斑竹奖
  • 社区明星
阅读:3010回复:0

[C++技术]这种代码结构如何组织?goto or do…while

楼主#
更多 发布于:2012-09-06 11:34

灰常感谢各位达人昨天的热心回帖,让我受益匪浅。我仰望夜空,群星点点,就如各位的点睛之语,在无尽的苍穹闪耀。这让我深深地意识到,在这里,不仅可以分享成果,也可以分享困惑、分享寂寞。(开场白到此结束~)
在平常的编程中,我发现很容易遇到这种结构:
(1号方案)
BOOL foo()
{
    BOOL bRet = FALSE;


    HANDLE hProcess = OpenProcess(...);


    if (hProcess != NULL)
    {
        HANDLE hToken = OpenProcessToken(hProcess, ...);


        if (hToken != NULL)
        {
            // ...


            if (LookupPrivilegeValue(...))
            {
                if (AdjustTokenPrivileges(hToken, ...))
                {
                    bRet = TRUE;
                }
            }


            CloseHandle(hToken);
        }


        CloseHandle(hProcess);
    }


    return bRet;
}
如上写法,容易造成缩进级别不断增加。为了避免这种情况,可以改成:
(2号方案)
BOOL foo()
{
    HANDLE hProcess = OpenProcess(...);


    if (hProcess == NULL)
    {
        return FALSE;
    }


    HANDLE hToken = OpenProcessToken(hProcess, ...);


    if (hToken == NULL)
    {
        CloseHandle(hProcess);


        return FALSE;
    }


    // ...


    if (!LookupPrivilegeValue(...))
    {
        CloseHandle(hToken);
        CloseHandle(hProcess);


        return FALSE;
    }


    if (!AdjustTokenPrivileges(hToken, ...))
    {
        CloseHandle(hToken);
        CloseHandle(hProcess);


        return FALSE;
    }


    CloseHandle(hToken);
    CloseHandle(hProcess);


    return TRUE;
}
这样,又引来了新的问题,每次 return FALSE 时的清理任务比较麻烦,要是每步操作都引进新的 HANDLE 的话,后续的清理工作就变得非常繁重。有人推荐do…while(0)的结构,有人推荐goto。这两种形式分别是——
do…while(0):
(3号方案)
BOOL foo()
{
    HANDLE hProcess = OpenProcess(...);


    if (hProcess == NULL)
    {
        return FALSE;
    }


    BOOL bRet = FALSE;


    do
    {
        HANDLE hToken = OpenProcessToken(hProcess, ...);


        if (hToken == NULL)
        {
            break;
        }


        // ...


        BOOL bRetInner = FALSE;


        do
        {
            if (!LookupPrivilegeValue(...))
            {
                break;
            }


            if (!AdjustTokenPrivileges(hToken, ...))
            {
                break;
            }


            bRetInner = TRUE;


        } while (0);


        CloseHandle(hToken);


        if (!bRetInner)
        {
            break;
        }


        bRet = TRUE;


    } while (0);


    CloseHandle(hProcess);


    return bRet;
}
这种结构可以避免每次 return FALSE 前的一堆清理工作,但缺点是,有几个依赖性的 HANDLE,就要嵌套几层的 do…while(0),有时候也会遇到需要三四层嵌套的情形。
goto:
(4.1号方案)


BOOL foo()
{
    BOOL bRet = FALSE;


    HANDLE hProcess = OpenProcess(...);


    if (hProcess == NULL)
    {
        goto CLEAR;
    }


    HANDLE hToken = OpenProcessToken(hProcess, ...);


    if (hToken == NULL)
    {
        goto CLEAR;
    }


    // ...


    if (!LookupPrivilegeValue(...))
    {
        goto CLEAR;
    }


    if (!AdjustTokenPrivileges(hToken, ...))
    {
        goto CLEAR;
    }


    bRet = TRUE;


CLEAR:
    if (hToken != NULL)
    {
        CloseHandle(hToken);
    }


    if (hProcess != NULL)
    {
        CloseHandle(hProcess);
    }


    return bRet;
}  (4.2号方案)


BOOL foo()
{
    BOOL bRet = FALSE;


    HANDLE hProcess = OpenProcess(...);


    if (hProcess == NULL)
    {
        goto ERROR_LEVEL0;
    }


    HANDLE hToken = OpenProcessToken(hProcess, ...);


    if (hToken == NULL)
    {
        goto ERROR_LEVEL1;
    }


    // ...


    if (!LookupPrivilegeValue(...))
    {
        goto ERROR_LEVEL2;
    }


    if (!AdjustTokenPrivileges(hToken, ...))
    {
        goto ERROR_LEVEL2;
    }


    bRet = TRUE;


ERROR_LEVEL2:
    CloseHandle(hToken);
ERROR_LEVEL1:
    CloseHandle(hProcess);
ERROR_LEVEL0:
    return bRet;
}
(左边和右边哪种好一点。。。?)
在这种情形下,goto 的方案似乎是完美的。但是 goto 如果遇到 C++,缺点体现出来了。下面这一段,现在是 do…while(0) 结构(只有一层嵌套,这种结构用在这里还算合理):
BOOL foo()
{
    HRESULT hr = CoInitializeEx(0, COINIT_MULTITHREADED);


    while (true)
    {
        if (FAILED(hr))
        {
            break;
        }


        hr = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL);


        if (FAILED(hr))
        {
            break;
        }


        CComPtr<IWbemLocator> pLoc = NULL;
        hr = pLoc.CoCreateInstance(CLSID_WbemLocator, NULL, CLSCTX_INPROC_SERVER);


        if (FAILED(hr))
        {
            break;
        }


        CComPtr<IWbemServices> pSvc = NULL;
        hr = pLoc->ConnectServer(_T("ROOT\\CIMV2"), NULL, NULL, NULL, 0, NULL, NULL, ;pSvc);


        if (FAILED(hr))
        {
            break;
        }


        hr = CoSetProxyBlanket(pSvc, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE);


        if (FAILED(hr))
        {
            break;
        }


        CComPtr<IEnumWbemClassObject> pEnum = NULL;
        _bstr_t bstrLang = _T("WQL");
        _bstr_t bstrSql = _T("SELECT * FROM __InstanceCreationEvent WITHIN 10")
            _T("WHERE TargetInstance ISA 'Win32_LogonSession' AND (TargetInstance.LogonType = 2 OR TargetInstance.LogonType = 11)");
        hr = pSvc->ExecNotificationQuery(bstrLang, bstrSql, WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, ;pEnum);


        if (FAILED(hr))
        {
            break;
        }


        ULONG uCount = 1;
        CComPtr<IWbemClassObject> pNext = NULL;
        hr = pEnum->Next(WBEM_INFINITE, uCount, ;pNext, ;uCount);


        if (FAILED(hr))
        {
            break;
        }


        // ...


        break;
    }


    CoUninitialize();


    return SUCCEEDED(hr);
}
如果改成 goto,则需要把所有需要对象的定义全放到最前面来,不然 goto 会跳过他们的初始化,编译不过。但是,所有对象都放到最前面定义,又违反了即用即声明的规则,而且太多了也容易混淆。
最后,问题是,如果遇到 C++ 的、多层嵌套的,大家一般如何组织代码呢?
谢谢!








摘自 溪流漫话




喜欢0 评分0
游客

返回顶部