Lab 15-3: Exception - automatically destruct variables on the intermediate stack frame

 
實習目標 練習使用 C++ 例外狀況 (exception) 的處理機制
   利用例外發生時自動解構堆疊上物件的性質釋放動態記憶體
 
步驟一 下面程式片段中如果在 func3() 函式執行時出現例外狀況, 動態配置的 ptr 並不會自動釋放, 因此除了正常流程中結束時需要釋放 ptr 的記憶體外, 在 catch 敘述中也需要做 delete[] ptr; 的動作
    void main()
    {
        cout << "Entering main()\n";
        srand(time(0));
        int *ptr=0;
        try
        {
            ptr = new int[200];
            fun3();
            // using ptr
            delete[] ptr;
            ptr = 0;
        }
        catch (int)
        {
            delete[] ptr;
            ptr = 0;
            cout << "    main() deleting all resources\n";
            cout << "    main() exception catched\n";
        }
        cout << "Leaving main()\n";
    }
步驟二 下面函式中由於逐步地配置 ptr1 及 ptr2 的記憶體, 所以當執行 fun1() 發生例外狀況時, 有可能只需要刪除 ptr1, 也有可能需要刪除 ptr1 及 ptr2, 判斷的方法很多種, 但是例外處理的邏輯會變得比較複雜
    void fun1(int id)
    {
        cout << "Entering fun1(" << id << ")\n";
        if (rand()%2 == 0)
        {
            cout << "    fun1() exception occurs\n";
            throw id;
        }
        cout << "Leaving fun1() normally\n";
    }

    void fun2()
    {
        cout << "Entering fun2()\n";
        int *ptr1, *ptr2;
        try
        {
            ptr1 = new int[100];
            fun1(1);
            ptr2 = new int[200];
            fun1(2);
            // using ptr1 and ptr2
            delete[] ptr1;
            ptr1 = 0;
            delete[] ptr2;
            ptr2 = 0;
        }
        catch (int id)
        {
            if ((id==1)||(id==2))
            {
                delete[] ptr1; 
                ptr1 = 0;
            }
            if (id==2)
            {
                delete[] ptr2; 
                ptr2 = 0;
            }
            cout << "    fun2() exception catched\n";
            cout << "    fun2() deleting all resources\n";
            cout << "    Leaving fun2() with exception rethrowed\n";
            throw;
        }
        cout << "Leaving fun2() normally\n";
    }

    void fun3()
    {
        cout << "Entering fun3()\n";
        int *iary = new int[300];
        fun2();
        iary[1] = 10;
        delete[] iary;
        cout << "Leaving fun3() normally\n";
    }
在執行 fun1() 發生例外狀況時, 由於 main() 函式也沒辦法繼續執行下去, 所以需要把例外狀況透過 throw; 敘述繼續傳出去, 強迫所有在 call stack 上的函式順序處理例外狀況

請注意在執行 fun1() 發生例外狀況時, fun3() 裡的 iary 會沒有釋放掉, 所以會有 memory leakage, 請用 MFC 的 DLL 來觀察這個記憶體錯誤

步驟三 由上面的 fun2() 和 fun3() 我們知道動態配置的記憶體在例外狀況發生時, 處理起來是比較麻煩的, 如果能夠改由堆疊上配置的話, C++ 例外處理的機制可以自動幫我們解構, 如下例, 我們製作一個 IntArray 的類別, 以它取代 int iary[100];, 觀察是否在例外發生時會自動解構
    void fun3()
    {
        cout << "Entering fun3()\n";
        IntArray iary(100);
        fun2();
        iary[1] = 10;
        cout << "Leaving fun3() normally\n";
    }

    class IntArray
    {
    public:
        IntArray(int size):data(0)
        {
            assert(size>0);
            data = new int[size];
        }
        ~IntArray() 
        { 
            delete[] data; 
        }
        int &operator[](int index)
        {
            return data[index];
        }
    private:
        int *data;
    };
請在建構元及解構元中列印訊息出來觀察物件解構的時間點
步驟四 範例執行程式
步驟五 正常執行結果:
    Entering main()
    Entering fun3()
        ManagedIntArray(100)
    Entering fun2()
    Entering fun1(1)
    Leaving fun1() normally
    Entering fun1(2)
    Leaving fun1() normally
    Leaving fun2() normally
    Leaving fun3() normally
        ~ManagedIntArray()
    Leaving main()
在第一次呼叫 fun1() 發生例外之執行結果
    Entering main()
    Entering fun3()
        ManagedIntArray(100)
    Entering fun2()
    Entering fun1(1)
        fun1() exception occurs
        fun2() exception catched
        fun2() deleting all resources
        Leaving fun2() with exception rethrowed
        ~ManagedIntArray()
        main() deleting all resources
        main() exception catched
    Leaving main()
在第二次呼叫 fun1() 發生例外之執行結果
    Entering main()
    Entering fun3()
        ManagedIntArray(100)
    Entering fun2()
    Entering fun1(1)
    Leaving fun1() normally
    Entering fun1(2)
        fun1() exception occurs
        fun2() exception catched
        fun2() deleting all resources
        Leaving fun2() with exception rethrowed
        ~ManagedIntArray()
        main() deleting all resources
        main() exception catched
    Leaving main()
步驟六 請助教檢查後, 將所完成的 project (去掉 debug/ 資料匣下的所有內容) 壓縮起來, 選擇 Lab15-3 上傳, 後面的實習課程可能需要使用這裡所完成的程式

C++ 物件導向程式設計課程 首頁

製作日期: 06/01/2004 by 丁培毅 (Pei-yih Ting)
E-mail: pyting@mail.ntou.edu.tw TEL: 02 24622192x6615
海洋大學 電機資訊學院 資訊工程系 Lagoon