青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

拂曉·明月·彎刀

觀望,等待只能讓出現(xiàn)的機(jī)會(huì)白白溜走

  C++博客 :: 首頁(yè) ::  :: 聯(lián)系 :: 聚合  :: 管理 ::
轉(zhuǎn)自:http://msdn.microsoft.com/zh-cn/library/ms809695.aspx#mainSection

發(fā)布日期 : 9/9/2004 | 更新日期 : 9/9/2004

Jon Pincus
Microsoft Corporation

摘要:討論錯(cuò)誤處理范例以及與多個(gè)范例相關(guān)聯(lián)的陷阱,并提供了兩個(gè)簡(jiǎn)單但至關(guān)重要的有關(guān)錯(cuò)誤情況處理的原則。

本頁(yè)內(nèi)容

簡(jiǎn)介 簡(jiǎn)介 
錯(cuò)誤處理范例 錯(cuò)誤處理范例 
多個(gè) API 約定的問(wèn)題 多個(gè) API 約定的問(wèn)題 
有關(guān)錯(cuò)誤情況的兩個(gè)簡(jiǎn)單原則 有關(guān)錯(cuò)誤情況的兩個(gè)簡(jiǎn)單原則 
小結(jié) 小結(jié) 

簡(jiǎn)介

有能力的程序員能夠編寫(xiě)在未發(fā)生異常情況時(shí)正常運(yùn)行的代碼。使程序員出類(lèi)拔萃的技能之一是能夠編寫(xiě)在發(fā)生錯(cuò)誤和出現(xiàn)“意外事件”時(shí)仍然能繼續(xù)運(yùn)行的代碼。然而,術(shù)語(yǔ)“意外事件”會(huì)給人一種錯(cuò)誤的印象。如果您的代碼嵌入在一個(gè)廣泛分布的成功產(chǎn)品中,那么您應(yīng)該預(yù)料到代碼可能發(fā)生的各種異常(且可怕)的情況。計(jì)算機(jī)將耗盡內(nèi)存,文件未如您所愿地存在于應(yīng)該存在的地方,從未失敗的函數(shù)有可能在新版本的操作系統(tǒng)中失敗,等等,不一而足。如果您希望代碼能繼續(xù)可靠地運(yùn)行,那么就需要預(yù)見(jiàn)所有這些事件。

本文討論的特定類(lèi)別的異常事件是錯(cuò)誤情況。錯(cuò)誤情況并沒(méi)有一個(gè)準(zhǔn)確的定義。直觀地講,它就是指通常能夠成功的事情并未成功。內(nèi)存分配失敗就是一個(gè)典型示例。通常,系統(tǒng)有大量?jī)?nèi)存可以滿(mǎn)足需求,但是偶爾計(jì)算機(jī)也可能會(huì)由于某種原因而特別繁忙,例如,運(yùn)行大型的電子表格計(jì)算,或者在 Internet 世界中有人正在對(duì)該計(jì)算機(jī)發(fā)動(dòng)拒絕服務(wù)攻擊。如果您要編寫(xiě)任意類(lèi)型的重要組件、服務(wù)或應(yīng)用程序,那么就需要預(yù)見(jiàn)到這種情況。

本文采用的編碼示例基于 C/C++,但一般原則是獨(dú)立于編程語(yǔ)言的。即使不使用 C/C++ 代碼,使用多種語(yǔ)言的程序員也應(yīng)該記住這一點(diǎn)。例如,PC Week 舉辦的黑客競(jìng)賽 (Hackpcweek.com) 的獲勝者,就是利用某些 Perl CGI 代碼中檢查返回代碼的失敗,來(lái)部分地對(duì) Apache 服務(wù)器發(fā)起攻擊。有關(guān)進(jìn)一步的信息,請(qǐng)參閱 http://www.zdnet.com/eweek/stories/general/0,11011,2350744,00.html 上的文章。

任何重要的軟件部分都會(huì)與其他的層和組件進(jìn)行交互。編寫(xiě)處理錯(cuò)誤的代碼的第一步,是了解當(dāng)錯(cuò)誤發(fā)生時(shí)系統(tǒng)的其余部分正在執(zhí)行哪些操作。本文的其余部分將討論錯(cuò)誤處理范例,然后討論與多個(gè)范例相關(guān)聯(lián)的陷阱。本文還包括了兩個(gè)簡(jiǎn)單但至關(guān)重要的有關(guān)錯(cuò)誤情況處理的原則。

錯(cuò)誤處理范例

無(wú)論何時(shí)發(fā)生錯(cuò)誤情況,都需要考慮三個(gè)獨(dú)立的問(wèn)題:檢測(cè)錯(cuò)誤情況、報(bào)告錯(cuò)誤情況以及對(duì)錯(cuò)誤情況作出響應(yīng)。通常,處理這些問(wèn)題的職責(zé)分散在代碼的不同組件或?qū)又小@纾瑱z測(cè)系統(tǒng)是否內(nèi)存不足是操作系統(tǒng)的工作。內(nèi)存分配函數(shù)的工作是將這種情況報(bào)告給它的調(diào)用方。例如,VirtualAlloc 會(huì)返回 NULL,Microsoft 基礎(chǔ)類(lèi)庫(kù) (MFC) 運(yùn)算符new 會(huì)引發(fā)CMemoryException *,而 HeapAlloc 可能會(huì)返回 NULL 或引發(fā)結(jié)構(gòu)化異常。調(diào)用方的工作就是對(duì)此作出響應(yīng),方法是清理自身的工作,捕捉異常,并且還可能將失敗報(bào)告給它的調(diào)用方或用戶(hù)。

因?yàn)椴煌膶雍徒M件需要相互協(xié)作以處理這些錯(cuò)誤,所以第一步是定義特殊詞匯,即,所有組件共同遵守的約定。遺憾的是,在 C、C++ 或其他任何語(yǔ)言中都沒(méi)有單一且定義完善的約定。相反,卻存在許多約定,并且每種約定都有各自的優(yōu)缺點(diǎn)。

下面列出了一些最常用的約定:

  • 返回一個(gè) BOOL 值以指示成功或失敗。Windows API 和 MFC 中的很多調(diào)用都返回 TRUE 以指示成功,返回 FALSE 以指示失敗。這種方法既好又簡(jiǎn)單。但問(wèn)題在于,該方法不對(duì)失敗進(jìn)行解釋?zhuān)膊粎^(qū)分不同類(lèi)型的成功或失敗。(當(dāng)使用 Windows API 時(shí),您可以使用 GetLastError 來(lái)獲得特定代碼。)

  • 返回狀態(tài)。遺憾的是,隨著 Windows API 的變化,該約定出現(xiàn)了兩種不同的樣式。COM 函數(shù)返回 HRESULT:HRESULT >= 0(例如,S_OK)表示成功,HRESULT < 0(例如,E_FAIL)表示失敗。其他函數(shù)(例如,Registry 函數(shù))則返回 WINERROR.H 中定義的錯(cuò)誤代碼。在該約定中,ERROR_SUCCESS (0) 是唯一的成功值,所有其他值都表示各種形式的失敗。這種不一致有可能造成各種各樣的混亂。更糟糕的是,值 0(它在 Boolean 樣式的返回值約定中表示失敗)在此處表示成功。其后果將在后面進(jìn)行討論。在任何事件中,狀態(tài)返回方法都具有優(yōu)勢(shì) — 不同的值可以表示不同類(lèi)型的失敗(或者對(duì)于 HRESULT 而言,表示成功)。

  • 返回一個(gè) NULL 指針。C 樣式內(nèi)存分配函數(shù)(例如,HeapAllocVirtualAllocGlobalAlloc  malloc)通常返回一個(gè) NULL 指針。另一個(gè)示例是 C 標(biāo)準(zhǔn)庫(kù)的 fopen 函數(shù)。與 BOOL 返回值相同,還需要其他一些機(jī)制來(lái)區(qū)分不同種類(lèi)的失敗。

  • 返回一個(gè)  不可能的值  該值通常為 0(對(duì)于整數(shù),如 GetWindowsDirectory)或 –1(對(duì)于指針或長(zhǎng)度,如 C 標(biāo)準(zhǔn)庫(kù)的 fgets 函數(shù))。NULL 指針約定的泛化是它找到某個(gè)值 — 例程采用其他方式將無(wú)法返回該值,并且讓該值表示錯(cuò)誤。因?yàn)橥ǔ](méi)有中心模式,所以在將該方法擴(kuò)展到大型 API 時(shí)會(huì)在某種程度上出現(xiàn)問(wèn)題。

  • 引發(fā) C++ 異常。對(duì)于純粹的 C++ 程序來(lái)說(shuō),該約定可讓您使用語(yǔ)言功能來(lái)獲益。Bobby Schmidt 最近撰寫(xiě)的有關(guān)異常的“Deep C++”系列文章詳細(xì)討論了這些問(wèn)題。然而,將該方法與舊式的 C 代碼或者與 COM 結(jié)合可能會(huì)帶來(lái)問(wèn)題。對(duì)于 COM 方法而言,引發(fā) C++ 異常是非法的。此外,C++ 異常是開(kāi)銷(xiāo)相對(duì)較大的一種機(jī)制。如果操作本身的價(jià)值不高,那么過(guò)大的開(kāi)銷(xiāo)通常會(huì)抵消所獲得的好處。

  • 引發(fā)結(jié)構(gòu)化異常。這里的注意事項(xiàng)恰與 C++ 異常相反。該方法對(duì)于 C 代碼而言非常整潔,但與 C++ 的交互性不太好。同樣,該方法不能與 COM 有效地結(jié)合。

 如果您要選用較舊的代碼基,那么您有時(shí)會(huì)看到“原生的異常處理機(jī)制”。C++ 編譯器只是在最近才開(kāi)始比較好地處理異常。在過(guò)去,開(kāi)發(fā)人員經(jīng)常基于 Windows 結(jié)構(gòu)化異常處理 (SEH) 或 C 庫(kù)的 setjmp/longjmp 機(jī)制來(lái)構(gòu)建他們自己的機(jī)制。如果您已經(jīng)繼承了這些代碼基中的一個(gè),則需要自擔(dān)風(fēng)險(xiǎn),重新編寫(xiě)它可能是最好的選擇。否則,最好由經(jīng)驗(yàn)非常豐富的程序員來(lái)處理。

錯(cuò)誤處理是任何 API 定義的關(guān)鍵部分。無(wú)論您是要設(shè)計(jì) API 還是使用他人設(shè)計(jì)的 API,都是如此。對(duì)于 API 定義而言,錯(cuò)誤行為范例與類(lèi)定義或命名方案同樣重要。例如,MFC API 非常明確地規(guī)定了在資源分配失敗時(shí)哪些函數(shù)引發(fā)哪些異常,以及哪些函數(shù)返回 BOOL 成功/失敗調(diào)用。API 的設(shè)計(jì)者明確地將某些想法植入這一規(guī)定,用戶(hù)需要理解其意圖并按照已經(jīng)構(gòu)建的規(guī)則進(jìn)行操作。

如果您要使用現(xiàn)有的 API,則必須處理所有現(xiàn)有約定。如果您要設(shè)計(jì) API,則應(yīng)該選用能夠與您已經(jīng)使用的 API 相適應(yīng)的約定。

如果您要使用多個(gè) API,則通常要使用多個(gè)約定。某些約定組合可以很好地工作,因?yàn)檫@些約定很難混淆。但是,某些約定組合卻很容易出錯(cuò)。本文所討論的許多特定陷阱就是由于存在這些不一致而造成的。

多個(gè) API 約定的問(wèn)題

混用和匹配不同的 API 約定通常是無(wú)法避免的,但這非常容易出錯(cuò)。一些實(shí)例是顯而易見(jiàn)的。例如,如果您嘗試在同一個(gè)可執(zhí)行文件中混用 Windows SEH 和 C++ 異常,則很可能會(huì)失敗。其他示例更為微妙。其中一個(gè)反復(fù)出現(xiàn)的特定示例就與 HRESULT 有關(guān),并且是以下示例的某種變體:

extern BOOL DoIt();
BOOL ok;
ok = DoIt(...);
if (FAILED(ok))     // WRONG!!!
return;

該示例為何是錯(cuò)誤的?FAILED 是一個(gè) HRESULT 樣式的宏,因此它會(huì)檢查其參數(shù)是否小于 0。以下是它的定義(摘自 winerror.h):

#define FAILED(Status) ((HRESULT)(Status)<0)

因?yàn)?FALSE 被定義為 0,所以 FAILED(FALSE) == 0 是違反直覺(jué)的,這無(wú)須多言。而且,因?yàn)樵摱x嵌入了強(qiáng)制轉(zhuǎn)換,所以即使您使用警告級(jí)別 4,也不會(huì)獲得編譯器警告。

當(dāng)您處理 BOOL 時(shí),不應(yīng)該使用宏,但應(yīng)該進(jìn)行顯式檢查:

BOOL ok;
ok = DoIt(...);
if (! ok)
return;

相反,當(dāng)您處理 HRESULT 時(shí),則應(yīng)該始終使用 SUCCEEDED 和 FAILED 宏。

HRESULT hr;
hr = ISomething->DoIt(...);
if (! hr)     // WRONG!!!
return;

這是一個(gè)惡性錯(cuò)誤,因?yàn)樗苋菀妆缓雎浴H绻?span id="hvzpftn" class=Apple-converted-space> CoDoIt 返回 S_OK,則測(cè)試將成功完成。但是,如果 CoDoIt 返回某個(gè)其他成功狀態(tài),會(huì)怎樣呢?那樣,hr > 0,所以 !hr == 0;if 測(cè)試失敗,代碼將返回實(shí)際上并未發(fā)生的錯(cuò)誤。

下面是另一個(gè)示例:

HRESULT hr;
hr = ISomething->DoIt(...);
if (hr == S_OK)     // STILL WRONG!!!
return;

人們有時(shí)會(huì)插話(huà)說(shuō) ISomething::DoIt 在成功時(shí)總是返回 S_OK,因此最后兩個(gè)代碼片段肯定都沒(méi)有問(wèn)題。但是,這不是一個(gè)安全的假設(shè)。COM 接口的說(shuō)明非常清楚。函數(shù)在成功時(shí)可以返回任何成功值,因此 ISomething::DoIt 的眾多實(shí)現(xiàn)者中的任何一個(gè)都可能選擇返回某個(gè)值,例如 S_FALSE。在這種情況下,您的代碼將中止運(yùn)行。

正確的解決方案是使用宏,這也就是宏存在的原因。

HRESULT hr;
hr = ISomething->DoIt(...);
if (FAILED(hr))
return;

因?yàn)橐呀?jīng)引出了 HRESULT 的主題,所以現(xiàn)在是提醒您 S_FALSE 特性的大好時(shí)機(jī):

  • 它是一個(gè)成功代碼,而不是一個(gè)失敗代碼,因此 SUCCEEDED(S_FALSE) == 1。

  • 它被定義為 1,而不是 0,因此 S_FALSE == TRUE。

有關(guān)錯(cuò)誤情況的兩個(gè)簡(jiǎn)單原則

有許多簡(jiǎn)單的方法可以使代碼更可靠地處理錯(cuò)誤情況。相反,人們不愿意做的許多簡(jiǎn)單事情會(huì)使代碼在發(fā)生錯(cuò)誤情況時(shí)變得脆弱和不可靠。

總是檢查返回狀態(tài)

沒(méi)有比這更簡(jiǎn)單的事情了。幾乎所有函數(shù)都提供某種表明它們是成功還是失敗的指示,但如果您不檢查它們,則這一點(diǎn)沒(méi)有任何用處。這能有多困難呢?可以將其視為一個(gè)衛(wèi)生問(wèn)題。您知道在吃東西之前應(yīng)該洗手,但您可能并不總是這樣做。這與檢查返回值的道理相同。

下面是一個(gè)涉及到 GetWindowsDirectory 函數(shù)的簡(jiǎn)單而實(shí)用的示例。MSDN 文檔清楚地說(shuō)明了 GetWindowsDirectory 的錯(cuò)誤行為:

Return Values

如果該函數(shù)失敗,則返回值為 0。要獲得擴(kuò)展的錯(cuò)誤信息,請(qǐng)調(diào)用 GetLastError 函數(shù)。

實(shí)際上,文檔中寫(xiě)得非常清楚。

下面是一個(gè)判斷 Windows 目錄駐留在哪個(gè)驅(qū)動(dòng)器中的代碼片段。

TCHAR cDriveLetter;
TCHAR szWindowsDir[MAX_PATH];
GetWindowsDirectory(szWindowsDir, MAX_PATH);
cDriveLetter = szWindowsDir[0];   // WRONG!!!
...

如果 GetWindowsDirectory 失敗,會(huì)發(fā)生什么情況呢?(如果您不相信 GetWindowsDirectory 會(huì)失敗,這只是您暫時(shí)的觀點(diǎn)。)好,該代碼不檢查返回值,因此分配給 cDriveLetter 的值未初始化。未初始化的內(nèi)存可以具有任意值。實(shí)際上,該代碼將隨機(jī)選擇驅(qū)動(dòng)器。這樣做幾乎不可能是正確的。

正確的做法是檢查錯(cuò)誤狀態(tài)。

TCHAR cDriveLetter;
TCHAR szWindowsDir[MAX_PATH];
if (GetWindowsDirectory(szWindowsDir, MAX_PATH))
{
cDriveLetter = szWindowsDir[0];
...
}

這種情況還能發(fā)生嗎?不檢查返回值的最常見(jiàn)借口是“我知道那個(gè)函數(shù)絕對(duì)不會(huì)失敗”。GetWindowsDirectory 就是一個(gè)很好的示例。一直到 Windows® 98 和 Windows NT® 4.0,它實(shí)際上確實(shí)沒(méi)有失敗過(guò),因此許多人養(yǎng)成了一個(gè)不好的習(xí)慣,即,假設(shè)它永遠(yuǎn)不會(huì)失敗。

現(xiàn)在 Windows 終端服務(wù)器出現(xiàn)了,要判斷單個(gè)用戶(hù)的 Windows 目錄變得更為復(fù)雜。GetWindowsDirectory 必須完成更多的工作,可能包括分配內(nèi)存。而且,因?yàn)殚_(kāi)發(fā)這一函數(shù)的開(kāi)發(fā)人員非常負(fù)責(zé)任,所以他完成了正確的工作并檢查內(nèi)存分配是否成功,如果不成功,則返回描述完整的錯(cuò)誤狀態(tài)。

這就導(dǎo)致了另外一些問(wèn)題:如果 GetWindowsDirectory 在失敗時(shí)已經(jīng)將它的輸出初始化為空字符串,是否會(huì)有所幫助?答案是否定的。結(jié)果不會(huì)是未初始化的,但它們?nèi)詫⑹谴中拇笠獾膽?yīng)用程序所未曾料到的東西。假設(shè)您具有一個(gè)由 cDriveLetter – 'A' ; 索引的數(shù)組,那么現(xiàn)在該索引將突然變?yōu)樨?fù)值。

即使終端服務(wù)器對(duì)于您不是問(wèn)題,但同樣的情況可能會(huì)發(fā)生在 Windows API 的任何未來(lái)實(shí)現(xiàn)中。您希望禁止正在開(kāi)發(fā)的應(yīng)用程序在將來(lái)版本的操作系統(tǒng)或替代實(shí)現(xiàn)(如 Embedded NT)中運(yùn)行嗎?一種良好的習(xí)慣是記住以下事實(shí):代碼經(jīng)常在其預(yù)期的到期日之后繼續(xù)生存。

有時(shí),檢查返回值是不夠的。請(qǐng)考慮 Windows API ReadFile。您經(jīng)常會(huì)看到如下代碼:

LONG buffer[CHUNK_SIZE];
ReadFile(hFile, (LPVOID)buffer,
CHUNK_SIZE*sizeof(LONG), &cbRead, NULL);
if (buffer[0] == 0)   // DOUBLY WRONG!!!
...

如果讀取操作失敗,說(shuō)明緩沖區(qū)的內(nèi)容是未初始化的。多數(shù)情況下它可能為零,但這一點(diǎn)并不確定。

讀取文件失敗的原因有許多。例如,該文件可能是遠(yuǎn)程文件,而網(wǎng)絡(luò)可能發(fā)生故障。即使它是本地文件,磁盤(pán)也可能恰好不合時(shí)宜地?fù)p壞。如果是這種情況,則文件的格式可能完全不同于預(yù)期格式。他人可能無(wú)意中或別有用心地替換了您認(rèn)為應(yīng)該存在于某個(gè)位置的文件,或者該文件可能只有一個(gè)字節(jié)。更為奇怪的事情已經(jīng)發(fā)生了。

要處理這一情況,您不但需要檢查讀取操作是否成功,還必須檢查以確保您已經(jīng)讀取了正確數(shù)量的字節(jié)。

LONG buffer[CHUNK_SIZE];
BOOL ok;
ok = ReadFile(hFile, (LPVOID)buffer,
CHUNK_SIZE*sizeof(LONG), &cbRead, NULL);
if (ok && cbRead > sizeof(LONG)) {
if (buffer[0] == 0)
...
}
else
// handle the read failure; for example
...

無(wú)庸諱言,上述代碼有點(diǎn)兒復(fù)雜。但是,編寫(xiě)可靠的代碼要比編寫(xiě)并不總是能夠正常工作的代碼更為復(fù)雜。對(duì)上述代碼產(chǎn)生性能方面的疑問(wèn)是很正常的。雖然添加了幾個(gè)測(cè)試,但在全局上下文(函數(shù)調(diào)用、磁盤(pán)操作,至少?gòu)?fù)制 CHUNK_SIZE * sizeof(LONG) 個(gè)字節(jié))中,其影響是極小的。

通常,每當(dāng)需要進(jìn)行返回值檢查時(shí),總是涉及到一個(gè)函數(shù)調(diào)用,因此性能開(kāi)銷(xiāo)不太重要。在某些情況下,編譯器可能會(huì)內(nèi)聯(lián)該函數(shù),但是如果發(fā)生這種行為,并且由于返回常數(shù)而實(shí)際上不需要檢查返回值時(shí),編譯器會(huì)將測(cè)試優(yōu)化掉。

誠(chéng)然,還是有一些特殊情況:您通過(guò)刪除返回值檢查而節(jié)省的少數(shù) CPU 循環(huán)至關(guān)重要;編譯器無(wú)法為您提供幫助;您控制了要調(diào)用的函數(shù)的行為。在上述情況下,省略一些返回值檢查是有意義的。如果您認(rèn)為自己處于類(lèi)似的情形,則應(yīng)該與其他開(kāi)發(fā)人員進(jìn)行討論,重新審視真正的性能折衷,然后,如果您仍然確信這樣做是正確的,則在代碼中每個(gè)省略返回值檢查的地方加上明確的注釋?zhuān)f(shuō)明您的決定并證明它的正確性。

總是檢查內(nèi)存分配

無(wú)論是使用 HeapAllocVirtualAllocIMalloc::AllocSysAllocStringGlobalAllocmalloc 還是任何 C++ 運(yùn)算符 new,您都不能想當(dāng)然地認(rèn)為內(nèi)存分配成功。同樣的道理也適用于其他各種資源,包括 GDI 對(duì)象、文件、注冊(cè)表項(xiàng)等等。

下面是一個(gè)很好的獨(dú)立于平臺(tái)的錯(cuò)誤代碼示例,這些代碼是在 C 標(biāo)準(zhǔn)庫(kù)的基礎(chǔ)上編寫(xiě)的:

char *str;
str = (char *)malloc(MAX_PATH);
str[0] = 0;         // WRONG!!!
...

在此例中,如果內(nèi)存耗盡,則 malloc 將返回 NULL,而 str 的反引用將是 NULL 指針的反引用。這會(huì)造成訪問(wèn)沖突,從而導(dǎo)致程序崩潰。除非您是在內(nèi)核模式下運(yùn)行(例如,設(shè)備驅(qū)動(dòng)程序),否則訪問(wèn)沖突將導(dǎo)致藍(lán)屏或可利用的安全漏洞。

解決方案非常簡(jiǎn)單。檢查返回值是否為 NULL,并執(zhí)行正確的操作。

char *str;
str = (char *)malloc(MAX_PATH);
if (str != NULL)
{
str[0] = 0;
...
}

與返回值檢查一樣,許多人相信實(shí)際上不會(huì)發(fā)生內(nèi)存分配問(wèn)題。誠(chéng)然,該問(wèn)題并不總是發(fā)生,但這并不意味著它永遠(yuǎn)不會(huì)發(fā)生。如果您讓成千上萬(wàn)(或數(shù)以百萬(wàn))的用戶(hù)運(yùn)行您的軟件,即使該問(wèn)題每月僅對(duì)每個(gè)用戶(hù)發(fā)生一次,后果也是嚴(yán)重的。

許多人相信,在內(nèi)存耗盡時(shí)做什么都是無(wú)所謂的。程序應(yīng)該退出。但是,這在許多方面都不適用。首先,假設(shè)程序在內(nèi)存耗盡時(shí)退出,那么將不會(huì)保存數(shù)據(jù)文件。其次,人們通常期望服務(wù)和應(yīng)用程序能夠長(zhǎng)期運(yùn)行,因此它們能夠在內(nèi)存暫時(shí)不足時(shí)繼續(xù)正常運(yùn)行是至關(guān)重要的。第三,對(duì)于在嵌入式環(huán)境中運(yùn)行的軟件而言,退出不是可行的選擇。處理內(nèi)存分配可能非常麻煩,但這件事情必須做。

有時(shí),意外的 NULL 指針可能不會(huì)導(dǎo)致程序崩潰,但這仍然不是件好事情。

HBITMAP hBitmap;
HBITMAP hOldBitmap;
hBitmap = CreateBitmap(. . .);
hOldBitmap = SelectObject(hDC, hBitmap);   // WRONG!!!
...

SelectObject 的文檔在對(duì) NULL 位圖執(zhí)行哪些操作方面含糊不清。這可能不會(huì)導(dǎo)致崩潰,但它顯然是不可靠的。代碼很可能出于某種原因而創(chuàng)建位圖,并希望用它來(lái)進(jìn)行一些繪圖工作。但是,因?yàn)樗茨軇?chuàng)建位圖,所以繪圖操作將不會(huì)發(fā)生。即使代碼沒(méi)有崩潰,這里也明顯存在一個(gè)錯(cuò)誤。同樣,您需要進(jìn)行檢查。

HBITMAP hBitmap;
HBITMAP hOldBitmap;
hBitmap = CreateBitmap(. . .);
if (hBitmap != NULL)
{
hOldBitmap = SelectObject(hDC, hBitmap);
...
}
else
...

當(dāng)您使用 C++ 運(yùn)算符 new 時(shí),事情開(kāi)始變得更加有趣。例如,如果您要使用 MFC,則全局運(yùn)算符 new 將在內(nèi)存耗盡時(shí)引發(fā)一個(gè)異常。這意味著您不能執(zhí)行以下操作:

int *ptr1;
int *ptr2;
ptr1 = new int[10];
ptr2 = new int[10];   // WRONG!!!!

如果第二次內(nèi)存分配引發(fā)異常,則第一次分配的內(nèi)存將泄漏。如果您的代碼嵌入到將要長(zhǎng)期運(yùn)行的服務(wù)或應(yīng)用程序中,則這些泄漏會(huì)累積起來(lái)。

只是捕捉異常是不夠的,您的異常處理代碼還必須是正確的。不要掉到下面這個(gè)誘人的陷阱中:

int *ptr1;
int *ptr2;
try {
ptr1 = new int[10];
ptr2 = new int[10];
}
catch (CMemoryException *ex) {
delete [] ptr1;   // WRONG!!!
delete [] ptr2;    // WRONG!!!
}

如果第一次內(nèi)存分配引發(fā)了異常,您將捕捉該異常,但要?jiǎng)h除一個(gè)未初始化的指針。如果您足夠幸運(yùn),這將導(dǎo)致即時(shí)訪問(wèn)沖突和崩潰。更有可能的是,它將導(dǎo)致堆損壞,從而造成數(shù)據(jù)損壞和/或在將來(lái)難以調(diào)試的崩潰。盡力初始化下列變量是值得的:

int *ptr1 = 0;
int *ptr2 = 0;
try {
ptr1 = new int[10];
ptr2 = new int[10];
}
catch (CMemoryException *ex) {
delete [] ptr1;
delete [] ptr2;
}

應(yīng)該指出的是,C++ 運(yùn)算符 new 有許多微妙之處。您可以用多種不同的方式來(lái)修改全局運(yùn)算符 new 的行為。不同的類(lèi)可以具有它們自己的運(yùn)算符 new,并且如果您不使用 MFC,則可能會(huì)看到不同的默認(rèn)行為。例如,在內(nèi)存分配失敗時(shí)返回 NULL,而不是引發(fā)異常。有關(guān)該主題的詳細(xì)信息,請(qǐng)參閱 Bobby Schmidt 的“Deep C++”系列文章中有關(guān)處理異常的第 7 部分

小結(jié)

如果您要編寫(xiě)可靠的代碼,則至關(guān)重要的一點(diǎn)是從一開(kāi)始就考慮如何處理異常事件。您不能事后再考慮對(duì)異常事件的處理。在考慮此類(lèi)事件時(shí),錯(cuò)誤處理是一個(gè)關(guān)鍵的方面。

錯(cuò)誤處理很難正確執(zhí)行。盡管本文只是粗淺地討論了這一問(wèn)題,但其中介紹的原則奠定了一個(gè)強(qiáng)大的基礎(chǔ)。請(qǐng)記住以下要點(diǎn):

  • 在設(shè)計(jì)應(yīng)用程序(或 API)時(shí),應(yīng)預(yù)先考慮您喜歡的錯(cuò)誤處理范例。

  • 在使用 API 時(shí),應(yīng)了解它的錯(cuò)誤處理范例。

  • 如果您處于存在多個(gè)錯(cuò)誤處理范例的情況下,請(qǐng)警惕可能造成混亂的根源。

  • 總是檢查返回狀態(tài)。

  • 總是檢查內(nèi)存分配。

如果您執(zhí)行了上述所有操作,就能夠編寫(xiě)出可靠的應(yīng)用程序。

posted on 2011-04-19 23:40 一路風(fēng)塵 閱讀(328) 評(píng)論(0)  編輯 收藏 引用 所屬分類(lèi): 轉(zhuǎn)載
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            欧美日韩在线播| 91久久在线| 欧美国产欧美亚州国产日韩mv天天看完整| 欧美一区二区三区在线播放| 国产精品视屏| 欧美一区国产在线| 农夫在线精品视频免费观看| 亚洲经典自拍| 欧美日韩一区二区在线视频| 欧美一级电影久久| 欧美高清你懂得| 亚洲影院在线| 亚洲高清不卡| 国产欧美一区二区精品秋霞影院| 久久久噜噜噜久久中文字免| 亚洲精品日韩久久| 久久久噜噜噜久久久| 亚洲天堂网在线观看| 国产伦精品一区二区三| 欧美电影免费| 久久影视三级福利片| 一区二区三区欧美| 欧美主播一区二区三区美女 久久精品人| 国产乱子伦一区二区三区国色天香| 美女诱惑一区| 久久久久在线| 久久成人精品| 久久久xxx| 久久欧美中文字幕| 欧美在线一级视频| 欧美有码视频| 久久久久国内| 久久亚洲国产精品一区二区| 欧美在线视频免费| 午夜精品一区二区三区四区 | 亚洲国产婷婷香蕉久久久久久99| 国产精品美女www爽爽爽| 欧美四级剧情无删版影片| 欧美精品自拍偷拍动漫精品| 欧美精品在线免费观看| 欧美日韩裸体免费视频| 欧美视频二区36p| 国产精品午夜国产小视频| 国产女人aaa级久久久级| 国产一区二区三区最好精华液| 国产亚洲欧洲| 91久久夜色精品国产网站| 99精品久久久| 欧美一区二区三区四区在线观看| 久久久久一区二区| 亚洲国产欧美一区二区三区丁香婷 | 亚洲一区三区在线观看| 欧美一区二区日韩一区二区| 久久久爽爽爽美女图片| 欧美日韩亚洲高清| 国产日韩欧美亚洲| 亚洲伦理一区| 久久婷婷麻豆| 久久综合久久综合久久| 狂野欧美性猛交xxxx巴西| 欧美岛国激情| 136国产福利精品导航| 亚洲一区二区精品在线观看| 裸体丰满少妇做受久久99精品| 亚洲国内在线| 久久亚洲精品一区| 国产精品久久久久免费a∨| 在线视频观看日韩| 久久精品国语| 亚洲欧美日韩一区二区三区在线| 欧美激情在线免费观看| 好吊一区二区三区| 欧美在线观看一二区| av不卡在线看| 欧美三区美女| 午夜亚洲伦理| 亚洲欧美日韩精品在线| 国产精品裸体一区二区三区| 一区二区三区你懂的| 亚洲人成在线观看一区二区| 欧美国产日本| 亚洲视频二区| 欧美第一黄色网| 国产手机视频一区二区| 国产精品网站一区| 黄色亚洲精品| 国产日产欧美一区| 亚洲丶国产丶欧美一区二区三区| 亚洲国产精品成人| 国产精品国产自产拍高清av王其| 亚洲一区二区三区中文字幕| 亚洲国产婷婷香蕉久久久久久| 香蕉成人啪国产精品视频综合网| 亚洲欧美不卡| 亚洲国产精品成人综合| 亚洲丝袜av一区| 一本色道久久综合精品竹菊| 国产精品日韩久久久久| 乱码第一页成人| 欧美日韩另类视频| 久久久久久久999| 久久男人av资源网站| 黄色一区二区三区四区| 欧美成人中文字幕| 欧美日韩亚洲高清一区二区| 欧美在线视频网站| 亚洲欧美中文另类| 国产日韩在线亚洲字幕中文| 欧美成人a∨高清免费观看| 欧美午夜不卡| 亚洲精品在线免费| 激情欧美一区二区| 亚洲欧美久久久久一区二区三区| 亚洲高清免费视频| 99伊人成综合| 日韩午夜电影在线观看| 久久精品日韩| 久久九九免费视频| 国产精品日韩二区| 亚洲精品久久久久| 91久久精品日日躁夜夜躁国产| 欧美一区2区三区4区公司二百 | 午夜在线观看欧美| 欧美色网在线| 亚洲一区二区三| 亚洲尤物在线| 国产精品久久久久久久久婷婷 | 亚洲国内自拍| 麻豆成人在线| 亚洲精选成人| 亚洲一区免费视频| 国产精品日韩欧美一区二区三区| 亚洲视屏一区| 国产亚洲一区在线播放| 一区二区三区日韩在线观看| 日韩亚洲欧美成人| 欧美午夜精品久久久| 中文一区字幕| 久久久久久久尹人综合网亚洲| 狠狠综合久久| 欧美国产日韩a欧美在线观看| 亚洲欧美日韩久久精品| 久久av二区| 亚洲国产精品成人| 欧美日韩国产a| 亚洲综合99| 欧美激情91| 欧美一区高清| 日韩亚洲在线| 国模私拍一区二区三区| 欧美日韩大片| 久久成人在线| 日韩视频在线播放| 久热精品视频| 久久精品1区| 亚洲午夜av| 亚洲精品久久久久久久久久久久久| 国产精品国产三级国产专播精品人 | 国产亚洲精品7777| 欧美极品欧美精品欧美视频| 久久精品欧洲| 欧美一区影院| 亚洲婷婷综合色高清在线| 91久久综合亚洲鲁鲁五月天| 蜜臀久久久99精品久久久久久 | 免费不卡视频| 久久久午夜精品| 久久精品一本| 久久人人爽爽爽人久久久| 欧美中文日韩| 久久久久国内| 蜜臀va亚洲va欧美va天堂 | 国产情侣久久| 亚洲欧美日韩精品| 亚洲欧美久久| 久久久久久久久久看片| 久久久久久久97| 欧美a级大片| 欧美日韩国产在线观看| 欧美日韩精品一区| 久久精品日韩| 亚洲第一福利社区| 国产精品美女在线| 欧美日韩一区三区四区| 在线观看日韩av电影| 欧美一区二区三区在线观看视频| 亚洲精品三级| 欧美日韩国产综合一区二区| 亚洲国产乱码最新视频| 久久久久久久久久久久久女国产乱| 中文国产亚洲喷潮| 国产精品久久久久久久久久ktv | 亚洲乱码国产乱码精品精可以看| 另类图片综合电影| 91久久在线| 亚洲国产日韩欧美在线99| 女人色偷偷aa久久天堂| 日韩视频欧美视频| 一本久久a久久精品亚洲| 欧美日韩中文字幕在线视频|