實習目標 |
練習使用 C++ 例外狀況 (exception) 的處理機制:
當函式的回傳值有不可避免的功能時 尋找較適當的例外處理地點 |
---|---|
步驟一 |
有的時候函式的傳回值不可能拿來傳遞錯誤碼,
例如下面這個 Vector 類別的 int &operator[](int) 函式,
函式傳回所指定的元素的參考:
class Vector { public: Vector(int size); ~Vector(); int &operator[](int index); private: int *m_data; int m_size; }; Vector::Vector(int size) : m_size(size), m_data(0) { if (m_size>0) { m_data = new int[m_size+1]; for (int i=0; i<m_size; i++) m_data[i] = 0; } else m_size = 0; } Vector::~Vector() { delete[] m_data; } int& Vector::operator[](int index) { if ((index < m_size)&&(index >= 0)) return m_data[index]; else return m_data[m_size]; }這個函式的傳回值是 int& 型態的變數, 必須要 bind 在一個變數上, 如果發生傳入的 index 超出 (0, m_size) 範圍外的話, 如何通知呼叫的函式??? 如下例: Vector iVector(4); ... iVector[2] = 10; // OK iVector[4] = 20; // Error index雖然由於 operator[]() 傳回 m_data[m_size] 的參考, 使得程式不會出現 memory access violation 的錯誤, 但是接下去程式的邏輯應該不會正確地執行, 如何中斷這個程式的執行呢? |
步驟二 |
請利用 throw 語法改寫 operator[]() 中 else 的部份,
在發現陣列 index 超過範圍時請 throw 一個自定的
array_index_out_of_bound 例外類別
if ((index < m_size)&&(index>=0)) return m_data[index]; else throw array_index_out_of_bound(index, m_size); 這個 array_index_out_of_bound 例外類別請繼承標準 C++ 函式庫 stdexcept 中的 runtime_error 類別, 在建構時將非法存取的那個 i 值還有陣列的大小傳入建構元函式中, 並且合成一個錯誤訊息字串 (基本上在 C++ 裡有兩種方法你可以用來合成錯誤訊息, 1. 用 ostrstream, 例如: char buf[200]; ostrstream os(buf, 199); os << "Try to access the " << index << "-th element" << '\0'; // m_errorMsg = new char[...]; // strcpy(m_errorMsg, buf);2. 用 sprintf, 例如: char buf[200]; sprintf(buf, "Try to access the %d-th element", index); // m_errorMsg = new char[...]; // strcpy(m_errorMsg, buf);), 記錄在 m_errorMsg 中, 類別可以宣告成: class array_index_out_of_bound: public runtime_error { public: array_index_out_of_bound(int index, int size); ~array_index_out_of_bound(); const char *what() const; private: char *m_errorMsg; };並且覆寫 exception::what() 成員函式, 傳回錯誤訊息 |
步驟三 |
測試程式如下,
特地把產生例外的程式放在 fun1() 函式內,
出錯時在 Vector::operator[] 內會 throw 一個 array_index_out_of_bound
的暫時性物件,
fun1() 剩餘的迴圈會立即中斷回到 fun2() 函式中的 catch (array_index_out_of_bound &) 敘述,
由於它只處理部份的錯誤回復動作,
因此它繼續 throw 原來的 array_index_out_of_bound 例外物件,
fun2() 函式內剩餘的部份不會執行,
流程回到 main() 函式中 catch (exception &) 敘述
void fun1() { int i; Vector dataHolder(10); for (i=1; i<=10; i++) dataHolder[2*i] = 2*i; cout << "fun1()::after for loop\n"; } void fun2() { int *ptr=new int[100]; // ... try { fun1(); ... } catch (array_index_out_of_bound &e) { cout << "fun2()::" << e.what() << endl; delete[] ptr; cout << "fun2()::" << "ptr memory released" << endl; throw; } cout << "fun2()::after catch\n"; delete[] ptr; } void main() { try { fun2(); ... } catch (exception &e) { cout << "main()::" << e.what() << endl; } cout << "main()::after catch\n"; }範例執行程式 輸出範例如下: fun2()::array_index_out_of_bound:: Try to access the 11-th element of a 10-element vector fun2()::ptr memory released main()::array_index_out_of_bound:: Try to access the 11-th element of a 10-element vector array_index_out_of_bound:: dtor() main()::after catch請注意在 fun1() 的 throw 敘述產生的暫時性物件, 一直到 main() 的 catch (exception &) 敘述結束時才會解構掉 |
步驟四 | 請助教檢查後, 將所完成的 project (去掉 debug/ 資料匣下的所有內容) 壓縮起來, 選擇 Lab15-2 上傳, 後面的實習課程可能需要使用這裡所完成的程式 |
注意事項 | 這個實習主要的目的是讓你熟悉 C++ exception 的處理機制, 但是並不是告訴你撰寫一個動態管理記憶體的陣列物件時一定要用 exception 語法, 請不要誤會; 上課時我們談到 exception 語法所處理的例外狀況是執行時發生的錯誤, 是要給程式的使用者看到的, 不是給程式的設計者看的, 使用者不懂 C++ 的語法, 如果陣列的大小是由使用者指定的, 則不夠大使得存取發生錯誤的訊息可能就可以讓使用者看到, 可以要求使用者更改他的輸入, 如果陣列的大小是程式設計者設定的, 和使用者操作時輸入的參數無關, 那麼這個錯誤就不需要給使用者, 就應該用 assert 來設計, 代表程式設計者應該在開發的過程中就要處理好這個錯誤, 不要讓這個錯誤發生; 這和 exception 的語法沒有關係, 而是和你程式的設計有關。 |
回
C++ 物件導向程式設計課程
首頁
製作日期: 05/31/2004
by 丁培毅 (Pei-yih Ting)
E-mail: pyting@mail.ntou.edu.tw
TEL: 02 24622192x6615
海洋大學
電機資訊學院
資訊工程系
Lagoon