在傳統的C語言,我們通過檢查函數的傳回值以判斷調用是否成功,並處理各類異常情況,在Unix環境下可以通過設定error變數發出錯誤訊息,並通過setjmp, longjmp來跳出深層次調用。C++語言在此基礎上引入了Exception機制,遇到異常的程式模組可以throw一個異常,其他方法可以通過try catch來捕捉該異常。但是相比後來的Java與C#中的異常處理機制,C++的還是有不少待完善的地方:
1,缺少Exception基類,任何類都可以作為作為異常被拋出,且可以拋出對象,引用或者指標。
2,沒有像Java那樣約束方法,強制要求其處理或者拋出其內部所有可能發生的異常。比如:調用該方法的其他程式模組根本無法從介面中判斷其是否會拋出異常,也就不會注意處理其異常。
正因為這樣,造成C++程式員很少會使用其異常機制,我通常還是按照C語言的方式,利用傳回值來區分各類錯誤,有時候偶爾在類的建構函式中會使用異常,因為在建構函式中產生的錯誤可能會造成產生的對象不正常,而建構函式沒有傳回值,因此只能拋出異常,或者設定一個狀態標誌位。
此外C++中異常機制的一大弊端就是會造成資源泄漏,比如:
void f()
{
Resource * r = new Resource();
FILE * f = fopen(“datafile.txt”);
DoSomething(); //這裡可能會拋出異常,造成後面的代碼無法執行,資源得不到及時釋放。
delete r;
fclose(f);
}
為瞭解決資源泄漏的問題,當然不僅僅是因為異常引起的,還有可能是在程式編寫過程中,某些地方不小心直接return了,或者為了避免在每一個返回語句前都加上一段delete, fclose之類的語句。在此我們需要用的C++的RAII機制:
RAII: Resource Acqusition is Initialization資源申請即初始化
其利用的原理就是堆棧上的對象在離開範圍時均會被自動調用解構函式,因此我們可以在建構函式中直接申請資源,在解構函式中釋放資源,從而保證資源始終能夠得到正常釋放。
比如:
class File
{
public:
File(const char * file_name) { f = fopen(file_name); ….}
~File() {fclose(f);}
int write(const char *data, int len);
int read(char *data, int len);
private:
FILE *f;
}
另外std::auto_ptr也是基於這個原理構造的。
還有一點要提出的是,有些人喜歡在建構函式中將資源初始化為空白,單獨使用其他的方法來申請資源,比如在上述File的建構函式中令f=NULL,再添加一個open()方法來開啟檔案,我個人認為這是沒有必要的,因為如果這樣的話,那麼在write與read方法中肯定每次都需要判斷f是否不為NULL。具體的應用見仁見智,因此在C#與Java中貌似很多都有預設什麼都不乾的建構函式。