程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 這種代碼結構如何組織?goto or do…while(0)?

這種代碼結構如何組織?goto or do…while(0)?

編輯:C++入門知識

灰常感謝各位達人昨天的熱心回帖,讓我受益匪淺。我仰望夜空,群星點點,就如各位的點睛之語,在無盡的蒼穹閃耀。這讓我深深地意識到,在這裡,不僅可以分享成果,也可以分享困惑、分享寂寞。(開場白到此結束~)
在平常的編程中,我發現很容易遇到這種結構:
(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++ 的、多層嵌套的,大家一般如何組織代碼呢?
謝謝!

 

 

摘自 溪流漫話

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved