这种代码结构如何组织?goto or do…while
3435 点击·0 回帖
![]() | ![]() | |
![]() | 灰常感谢各位达人昨天的热心回帖,让我受益匪浅。我仰望夜空,群星点点,就如各位的点睛之语,在无尽的苍穹闪耀。这让我深深地意识到,在这里,不仅可以分享成果,也可以分享困惑、分享寂寞。(开场白到此结束~) 在平常的编程中,我发现很容易遇到这种结构: (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++ 的、多层嵌套的,大家一般如何组织代码呢? 谢谢! 摘自 溪流漫话 | |
![]() | ![]() |