1. 程式人生 > >[c/c++]建構函式、解構函式中可不可以丟擲異常

[c/c++]建構函式、解構函式中可不可以丟擲異常

usingnamespace std;


class A
{
public:
    A()
    
{
        cout 
<<"construction fun"<< endl;
        
throw1;
    }


    
~A()
    
{
        cout 
<<"destruction fun "<< endl;
        
throw2;
    }

}
;

int main()
{
    
try
    
{
        A a;
    }

    
catch(...)//catch all
{
        cout 
<<"caught!"<< endl;
    }


    system(
"pause");
    
return(0);
}

//輸出
//construction fun
//caught!
//註釋掉"throw 1",輸出
//construction fun
//destruction fun
//caught! #include <iostream>
 
“建構函式中如果丟擲異常,可見解構函式並沒有被呼叫,這樣子的話就產生了記憶體洩漏,所以如果要在建構函式丟擲異常之前,應該先把已經成功分配的資源釋放掉.
在C++中,經典的解決方案是使用STL的標準類auto_ptr,並且不要做過多的事情,只是能對成員變數的做初始化工作就好了。真的需要做其他複雜的初始化操作,完全可以提供一個Init或Start函式.

解構函式:
C++ 並 沒有禁止解構函式引發異常,但 是 C++ 十分不推薦這一做法。”

=================================================================================
=================================================================================
對於出錯處理,在C語言時代,使用的方法就是返回一個錯誤程式碼。預定義一系列的程式碼標識,當發生指定的錯誤時候,呼叫過程返回對應該型別錯誤的程式碼。
    這種方法簡單,但是不適合複雜的應用。它會導致若干的問題,比如:
    1.質量下降。使用錯誤程式碼,那麼必然需要在處理中對不同的程式碼進行分支處理。而分支過程包含錯誤可能性是其他方式的10倍。消除分支,程式碼將更加健壯。
    2.增加成本。一方面,由於分支,那麼白盒法測試時的測試條件個數增多,延長測試過程;另一方面,控制流程的複雜性導致維護成本上升。
    3. ....

    然而更糟糕的是,C++的構造/解構函式不允許有返回值,沒有返回型別,所以使用錯誤程式碼的方法被一錘打死了。也許有人會想到使用出參的方式返回錯誤代 碼,但是這個方法對於OO特性是存在衝突的,其詳細分析可參見參考資料2。因此,通知物件的構造失敗的唯一方法那就是在建構函式中丟擲異常。
   
    建構函式異常,可以總結如下:
    1.C++中通知物件構造失敗的唯一方法那就是在建構函式中丟擲異常;
    2.建構函式丟擲異常時,解構函式將不會被執行;
    3.丟擲異常時,其子物件將被逆序析構。(參考析構過程)

    解構函式異常相對要複雜一些,存在一種衝突狀態,程式將直接崩潰:異常的被稱為“棧展開(stack unwinding)”【備註】的過程中時,從解構函式丟擲異常,C++執行時系統會處於無法決斷的境遇,因此C++語言擔保,當處於這一點時,會呼叫 terminate()來殺死程序。因此,當處理另一個異常的過程中時,不要從解構函式丟擲異常。概括總結如下:
    1.C++中解構函式的執行不應該丟擲異常;
    2.當在某一個解構函式中會有一些可能(哪怕是一點點可能)發生異常時,那麼就必須要把這種可能發生的異常完全封裝在解構函式內部,決不能讓它丟擲函式之外(這招簡直是絕殺!呵呵!);
    3.丟擲異常時,其子物件將被逆序析構。(參考析構過程)

備註:
棧展開(stack unwinding):舉例來說,如果某人寫了throw Foo(),棧會被展開,以至throw Foo()和 } catch (Foo e) { 之間的所有的棧頁面被彈出。這被稱為棧展開(statck unwinding)