
| 實習目標 |
練習使用 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