完成了第三章異常及錯誤處理,節錄一節
建構函數與異常
這個主題在C++社群中經常被提起,而在Delphi社群中似乎從來沒有人注意過。也許是由於語言的特性,使得Delphi程式設計師不必關心這個問題。但我想Delphi程式設計師也應該對該問題有所了解,知道語言為我們提供了什麼而使得我們如此輕鬆,不必理會它。正所謂「身在福中須知福」。我們知道,類別的建構子是沒有回傳值的,如果建構子建構物件失敗,不可能依賴回傳錯誤碼。那麼,在程式中如何標識建構函式的失敗呢?最「標準」的方法就是:拋出一個異常。構造函數失敗,意味著物件的構造失敗,那麼拋出異常之後,這個「半死不活」的物件會被如何處理呢?在此,我想讀著有必要先對C++對這種情況的處理方式先有個了解。在C++中,建構子拋出異常後,析構函式不會被呼叫。這種做法是合理的,因為此時物件並沒有被完整地建構。如果建構函式已經做了一些諸如分配記憶體、開啟檔案等操作的話,那麼C++類別需要有自己的成員來記住做過哪些動作。當然,這樣做對於類別的實現者來說非常麻煩,因此一般C++類別的實現者都避免在建構函數中拋出異常(可以提供一個諸如Init和UnInit的成員函數,由構造函數或類別的客戶去呼叫它們,以處理初始化失敗的情況)。而每一本C++的經典著作所提供的方案是使用智慧指標(STL的標準類別auto_ptr)。在Object Pascal中,這個問題變得非常的簡單,程式設計師不必為此大費周折。如果Object Pascal的類別在建構函數中拋出異常,編譯器會自動呼叫類別的析構函數(由於析構函數不允許被重載,可以保證只有唯一一個析構函數,因此編譯器不會迷惑於多個析構函數之中)。析構函數中一般會析構成員對象,而Free()方法保證了不會對nil對象(即尚未被創建的成員對象)調用析構函數,因此在使得程式碼簡潔優美的前提下,又保證了安全。 type MyClass = classPRivateFStr : PChar; // 字串指標publicconstructor Create();destructor Destroy(); override;end;constructor MyClass.Create();beginFStr := StrAlloc(10); // 建構子中為字串指標分配記憶體StrCopy(FStr, 'ABCDEFGHI');raise Exception.Create('error'); //拋出異常,沒有理由,呵呵end;destructor A.Destroy();beginStrDispose(FStr); // 析構函數中釋放記憶體WriteLn('Free Resource');end;varObj : TMyClass;i : integer;begintryObj : = TMyClass.Create();Obj.Free();WriteLn('Succeeded');exceptObj := nil;WriteLn('Failed');end;Read(i); // 暫停螢幕,以便觀察運行結果end.在這段程式碼中,建構子拋出異常,執行的結果是:Free ResourceFailed此時的「Free Resource」輸出是由編譯器自動呼叫析構函數所產生的。因此,如果類別的說明文件或類別的作者告知你,類別的建構子可能會拋出異常,那就要記得用try…except包住它! C++與Object Pascal對於建構子拋出異常後的不同處理方式,其實正是兩種語言的設計思想的體現。 C++秉承C的風格,注重效率,一切交給程式設計師來掌握,編譯器不作多餘動作。 Object Pascal繼承Pascal的風格,注重程式的美學意義,編譯器幫助程式設計師完成複雜的工作。