當前位置:編程學習大全網 - 編程語言 - 防禦式編程的相關實例

防禦式編程的相關實例

1. 在非法輸入(Invalid Inputs)中保護妳的程序

壹個好程序,在非法輸入的情況下,要麽什麽都不輸出,要麽輸出錯誤信息。有幾種方法來防止非法的輸入:

(1)檢查來自於外部資源(external sources)的所有數據的值,例如來源於網絡的數據的值,來源於文件的數據的值。檢查的目的是保證數據值在壹個允許的範圍內。

(2)檢查每壹個例程(routine)的輸入參數值。

壹旦非法輸入被發現,那麽應該根據情況進行處理。防禦式編程的最佳的形式是在壹開始就不引入錯誤。

2. 斷言(Assertions)

壹個斷言通常是壹個例程(routines)或者壹個宏(marcos)。每個斷言通常含有兩個參數:壹個布爾表示式(a boolean expression)和壹個消息(a message)。壹個布爾表達式的反面表示了壹個錯誤。C 標準庫提供了壹個 assert 宏,它只帶有壹個參數,用法如下:

assert(1 == 0); // 註意 boolean expression 不要加引號

使用 assert 宏,需要包含頭文件 cassert 或者assert.h,執行上面語句的結果是程序終止運行,輸出與下面消息類似的消息:

Assertion failed: 1 == 0, file d:\我的文檔\visual studio projects\learning\assert\assert.cpp, line 9

通常來說,我們會定義自己的 assert 宏,其目的有兩個:

(1)新增參數,例如新增壹個消息參數,使得 assert 宏輸出更為豐富的信息。

(2)改變 assert 的行為內容。C 標準庫中的 assert 宏將中斷程序,實際上,我們可以讓程序繼續運行而不中斷或者進入調試狀態等,另外還可以控制消息輸出的目標,即控制消息是輸出到控制臺還是文本文件,甚至是通過網絡發出。

下面是壹個 C++ 實現的斷言:

#ifdef _DEBUG

#define Assert(exp, message) \

{ \

if (!(exp)) \

{ \

std::cout << Assertion failed: << #exp << \n \

<< Message: << message << \n \

<< line: << __LINE__ << \n \

<< file: << __FILE__ << \n; \

exit(EXIT_FAILURE); \

} \

}

#else

#define Assert(exp, message)

#endif

執行 Assert(1 == 0, Error); 結果為:

Assertion failed: 1 == 0

Message: Error

line: 24

file: d:\我的文檔\visual studio projects\learning\assert\assert.cpp

使用斷言應該註意壹下的幾個問題:

1)對非預期錯誤使用斷言

斷言中的布爾表達式的反面壹定要描述壹個非預期錯誤,下面所述的在壹定情況下為非預期錯誤的壹些例子:

(1)空指針。

(2)輸入或者輸出參數的值不在預期範圍內。

(3)數組的越界。

非預期錯誤對應的就是預期錯誤,我們通常使用錯誤處理代碼來處理預期錯誤,而使用斷言處理非預期錯誤。在代碼執行過程中,有些錯誤永遠不應該發生,這樣的錯誤是非預期錯誤。斷言可以被看成是壹種可執行的註釋,妳不能依賴它來讓代碼正常工作(《Code Complete 2》)。例如:

int nRes = f(); // nRes 由 f 函數控制, f 函數保證返回值壹定在 -100 ~ 100

Assert(-100 <= nRes && nRes <= 100); // 斷言,壹個可執行的註釋

由於 f 函數保證了返回值處於 -100 ~ 100,那麽如果出現了 nRes 不在這個範圍的值時,就表明壹個非預期錯誤的出現。後面會講到“隔欄”,那時會對斷言有更加深刻的理解。

2)不要把需要執行的代碼放入斷言中

斷言用於軟件的開發和維護,而通常不在發行版本中包含斷言。

需要執行的代碼放入斷言中是不正確的,因為在發行版本中,這些代碼通常不會被執行,例如:

Assert(f()); // f 函數通常在發行版本中不會被執行

而使用如下方法則比較安全:

res = f();

Assert(res); // 安全

3)對來源於內部系統的可靠的數據使用斷言,而不要對外部不可靠的數據使用斷言,對於外部不可靠數據,應該使用錯誤處理代碼。再次強調,把斷言看成可執行的註釋。

前條件(preconditions)和後條件(postconditions)

前條件是調用方代碼在調用例程(routines)或者實例化對象之前要確保為真的條件,後條件是例程執行後或者類實例化後應滿足的條件。下面是壹個例子:

// 前條件,這裏 nNum1 和 nNum2 的取值被前面代碼所約束並保證取值在 -50 ~ 50

Assert(-50 <= nNum1 && nNum1 <= 50, Add_nNum1);

Assert(-50 <= nNum2 && nNum2 <= 50, Add_nNum2);

int nRes = add(nNum1, nNum2);

// 後條件

Assert(-100 <= nRes && nRes <= 100, Add_nRes);

註意,由於 nNum1 和 nNum2 取值範圍已經被約束,因此可以使用斷言,但是如果 nNum1 和 nNum2 的值來源於不可靠的外部系統,那麽應該使用錯誤處理代碼,而不是使用斷言。

3. 錯誤處理技術

這裏主要講述如何處理預期錯誤。

(1)終止程序運行

有些錯誤非常嚴重,如果出現,那麽最好就的做法就是讓程序終止並且讓用戶重啟程序。例如,對於顯示 X 光片的繪圖程序,如果數據出錯,那麽就關閉程序,這個時候關閉程序要遠遠好於顯示錯誤的數據。

(2)繼續程序運行

有時候,錯誤出現了,但是沒有必要去關閉程序,那麽就有兩種處理方案:

a. 在例程中處理錯誤

例如讓例程返回壹個中立值,這是壹種可行的方法,中立值在有些語言裏面被描述為“類型的默認值”,例如整型的中立值為 0,指針的中立值為 NULL(或 null 等)

b. 在例程外處理錯誤

返回壹個錯誤碼也是可行的,返回錯誤碼意味著,錯誤將交由其他程序部分來處理,而不是本例程處理。

對於出現了錯誤,而沒有終止程序的運行,這時候,妳可以在日誌文件中添加壹個警告信息。

抉擇:正確性和健壯性

有些程序要求非常高的正確性,而有些程序要求較高的健壯性,通常兩者我們只能取其壹。

(1)正確性意味著結果永遠是正確的,如果出錯,寧願不給出結果也不要給定壹個不準確的值。

(2)健壯性意味著通過壹些措施,保證軟件能夠正常運行下去,即使有時候會有壹些不準確的值出現。

4. 隔欄(barricades)

隔欄本身就是壹組錯誤處理代碼,對於內部類只需要使用斷言而無需使用錯誤處理代碼。當斷言為假時,表明了問題出在了程序中而不是數據中,需要通過修改代碼來消除問題。在此,請讀者聯系本文開始 --- “在非法輸入(Invalid Inputs)中保護妳的程序”這壹部分進行思考。

  • 上一篇:如何區分BS和CS架構
  • 下一篇:未來有哪些專業前景光明?
  • copyright 2024編程學習大全網