對於初學軟體開發的人來說,異常處理的重要性往往會被很多人所忽視。在正規化的軟體開發過程中,異常處理往往扮演了一個重要的角色。我自己學習軟體開發也有兩年了,在此之前沒有意識到異常處理的重要性。於是為此查了一些資料,重新學習了一下異常處理。在此總結一下學習成果。
一般的,傳統錯誤處理方法大致可以分為返回碼機制和全域變數兩種。
1。返回碼機制
這種處理錯誤的方法比較實用和簡單,這也是我以前經常採取的手段之一。對於小型的程式來說這種異常處理機制的缺點暴露不明顯,對於一個需要多人開發的軟體程式來說,它的弊端就非常明顯了!因為對於一個模組的實現者來說有的人傳回值0代表錯誤;有的人傳回值0代表正確,非0代表錯誤。解決方案可以用一致性的條文來控制。通常的,這些返回碼就在一個公用的.h檔案中以宏的形式存在。這樣暫時解決了團隊之間的一致性,但是這些都不是標準,相容性太差。對於如此多的返回碼要分別解釋各自的意義,從調用者的角度來說,需要分別對返回碼進行檢查來處理異常,這樣的代碼往往就顯得非常的臃腫,大大降低了可讀性。
2。全域變數
通過用一個全域變數來表示一次操作是否成功。這個方法在多線程中就非常頭痛。另外在每次處理完異常之後就要複位這個變數,如果忘記這個步驟,就會引起其他動作的誤解。
C++的異常處理機制彌補了傳統錯誤處理方法的弊端,當然它的代價就是效能上的消耗。但是換來了代碼品質的改進,提高了代碼間的維護性,這種消耗完全值得。
結構化異常處理和C++異常處理的區別:
結構化異常處理(SEH)是可用於任何程式設計語言的作業系統裝置。而異常處理只能用於編寫C++代碼。如果在編寫C++程式,應該使用C++異常處理而不是SEH。理由是C++異常處理是語言的一部分,編譯器知道C++類對象是什麼。也就是編譯器能夠自動產生代碼來調用C++對象解構函式,保證對象的清除。但是也應該知道VC編譯器已經利用作業系統的SEH實現了C++異常處理。所以當建立一個try塊時,編譯器就產生一個__try塊。一個catch變成了SEH異常過濾器,並且catch中的代碼變成了__except塊中的代碼。每寫一條throw語句時,編譯器就產生一個對windows的RaiseException函數的調用。用於throw語句的變數傳遞給RaiseException作為附加的參數。對於SEH的詳細說明在《Windows核心編程》的第23章有詳細介紹。
在使用C++異常處理的時候要注意幾點:
1.異常的類型要一致。(見function1)
2.對於傳遞大型類對象時盡量使用引用的方式。(見function1)
3.盡量保持try塊中拋出的異常在被處理的狀態(提供足夠的catch子句)(見function2)
class PrintErr
{
public:
PrintErr(int i) {m_iNum = i;}
int m_iNum;
};
class TypeErr{};
void Myprint(char* p) throw(PrintErr)
{
if(p == NULL)
throw PrintErr(10);
//...
}
void function1()
{
try
{
Myprint(NULL);// 拋出一個PrintErr的異常
}
// catch(int i) { /*..*/ } 異常的類型要一致
catch(PrintErr& err)// 使用引用的方式傳遞異常,防止不必要的拷貝大型類對象
{
//...
throw;// 如果異常在這裡無法完成,可以rethrow這個異常(throw err)
}
catch(...)// 對於其他類型的異常進行處理
{
}
}
void function2()
{
try
{
Myprint(NULL);
}
// 沒有為PrintErr提供catch子句,系統自動調用庫函數terminate()。
catch (TypeErr& err)
{
//...
}
}
void function3() throw()// 這個函數被定義成不拋出任何異常
{
Myprint(NULL);// error
}
int main(int argc, char* argv[])
{
try
{
function1();
}
catch (PrintErr& err)
{
//...對rethrow的異常進行處理
}
return 0;
}
通常使用Visual C++編譯異常處理時會出現warning C4290: C++ Exception Specification ignored。這是因為Visual C++在編譯期檢查異常規範申明,但在運行期忽略它們。你可以給函數加上異常申明,編譯器會正確地分析它們,但在運行期這個申明沒有效果,就象根本沒有寫過。所以可以使用#pragma warning(disable:4290)屏蔽這條警告。
對於初學軟體開發的人來說,異常處理的重要性往往會被很多人所忽視。在正規化的軟體開發過程中,異常處理往往扮演了一個重要的角色。我自己學習軟體開發也有兩年了,在此之前沒有意識到異常處理的重要性。於是為此查了一些資料,重新學習了一下異常處理。在此總結一下學習成果。
一般的,傳統錯誤處理方法大致可以分為返回碼機制和全域變數兩種。
1。返回碼機制
這種處理錯誤的方法比較實用和簡單,這也是我以前經常採取的手段之一。對於小型的程式來說這種異常處理機制的缺點暴露不明顯,對於一個需要多人開發的軟體程式來說,它的弊端就非常明顯了!因為對於一個模組的實現者來說有的人傳回值0代表錯誤;有的人傳回值0代表正確,非0代表錯誤。解決方案可以用一致性的條文來控制。通常的,這些返回碼就在一個公用的.h檔案中以宏的形式存在。這樣暫時解決了團隊之間的一致性,但是這些都不是標準,相容性太差。對於如此多的返回碼要分別解釋各自的意義,從調用者的角度來說,需要分別對返回碼進行檢查來處理異常,這樣的代碼往往就顯得非常的臃腫,大大降低了可讀性。
2。全域變數
通過用一個全域變數來表示一次操作是否成功。這個方法在多線程中就非常頭痛。另外在每次處理完異常之後就要複位這個變數,如果忘記這個步驟,就會引起其他動作的誤解。
C++的異常處理機制彌補了傳統錯誤處理方法的弊端,當然它的代價就是效能上的消耗。但是換來了代碼品質的改進,提高了代碼間的維護性,這種消耗完全值得。
結構化異常處理和C++異常處理的區別:
結構化異常處理(SEH)是可用於任何程式設計語言的作業系統裝置。而異常處理只能用於編寫C++代碼。如果在編寫C++程式,應該使用C++異常處理而不是SEH。理由是C++異常處理是語言的一部分,編譯器知道C++類對象是什麼。也就是編譯器能夠自動產生代碼來調用C++對象解構函式,保證對象的清除。但是也應該知道VC編譯器已經利用作業系統的SEH實現了C++異常處理。所以當建立一個try塊時,編譯器就產生一個__try塊。一個catch變成了SEH異常過濾器,並且catch中的代碼變成了__except塊中的代碼。每寫一條throw語句時,編譯器就產生一個對windows的RaiseException函數的調用。用於throw語句的變數傳遞給RaiseException作為附加的參數。對於SEH的詳細說明在《Windows核心編程》的第23章有詳細介紹。
在使用C++異常處理的時候要注意幾點:
1.異常的類型要一致。(見function1)
2.對於傳遞大型類對象時盡量使用引用的方式。(見function1)
3.盡量保持try塊中拋出的異常在被處理的狀態(提供足夠的catch子句)(見function2)
class PrintErr
{
public:
PrintErr(int i) {m_iNum = i;}
int m_iNum;
};
class TypeErr{};
void Myprint(char* p) throw(PrintErr)
{
if(p == NULL)
throw PrintErr(10);
//...
}
void function1()
{
try
{
Myprint(NULL);// 拋出一個PrintErr的異常
}
// catch(int i) { /*..*/ } 異常的類型要一致
catch(PrintErr& err)// 使用引用的方式傳遞異常,防止不必要的拷貝大型類對象
{
//...
throw;// 如果異常在這裡無法完成,可以rethrow這個異常(throw err)
}
catch(...)// 對於其他類型的異常進行處理
{
}
}
void function2()
{
try
{
Myprint(NULL);
}
// 沒有為PrintErr提供catch子句,系統自動調用庫函數terminate()。
catch (TypeErr& err)
{
//...
}
}
void function3() throw()// 這個函數被定義成不拋出任何異常
{
Myprint(NULL);// error
}
int main(int argc, char* argv[])
{
try
{
function1();
}
catch (PrintErr& err)
{
//...對rethrow的異常進行處理
}
return 0;
}
通常使用Visual C++編譯異常處理時會出現warning C4290: C++ Exception Specification ignored。這是因為Visual C++在編譯期檢查異常規範申明,但在運行期忽略它們。你可以給函數加上異常申明,編譯器會正確地分析它們,但在運行期這個申明沒有效果,就象根本沒有寫過。所以可以使用#pragma warning(disable:4290)屏蔽這條警告。