超限測試是vector::operator [] 與 matrix::at極為重要的面向。以vector::operator []為例,由於當初我選擇以operator [] 取用向量的分量,一開始向量v的第一分量為v[0],後來認錯修正為v[1],這樣的歷程即便是你自己都有可能記錯。於是,你有可能仍用v[0]表達向量v的第一分量 -- 超限了。
簡單的說起來,我們需要在取用向量的分量之前檢查operator []的傳入參數是否超限。如是,則應避免傳值回去。這種情況類似於先前計算innerProduct的函數,
bool innerProduct(double &product, const vector &u, const vector &v);
簡單的說起來,我們需要在取用向量的分量之前檢查operator []的傳入參數是否超限。如是,則應避免傳值回去。這種情況類似於先前計算innerProduct的函數,
bool innerProduct(double &product, const vector &u, const vector &v);
所以,一種解法是仿照innerProduct的函數的做法,傳回bool並引進一個參數作為回傳結果。
問題來了,由於operator []是C++ 內建運算子,只能接受一個輸入參數,且返回型態在這裡必須是 double:
double & operator [](int i) const;
所以無法套用innerProduct的函數的做法!我們得另想辦法。
幸運的是C++及其他較新的程式語言如Java、Python、Scala等,均提供表達與處理例外狀況的機制(exception handling mechanism)。如果將超限視為一種例外狀況,則 operator []可檢查(detect)是否超限,如是則拋出例外(throw an exception),表示超限已發生, 並終止正常函數執行:
double & vector::operator [](int i) const {
if (i <=0 || i > _dim )
throw std::string("Index to vector out of bound!");
return _v[i-1];
}
C++ 提供一個exception class,用來表達例外。我們可以利用這個型態,但是代價為較為複雜些。幸好,C++容許拋出任意型態的例外,例如int、const char* (C string)、string等。這裡,vector::operator[]可拋出的例外型態為string。
呼叫operator []的敘述,則可放置在 try 敘述 (try statement)的try block之中,並由緊跟在後的catch block加以捕捉:
try {
double component = u[0];
}
catch (string s) {
CHECK(string("Index to vector out of bound!") == s);
}
問題來了,由於operator []是C++ 內建運算子,只能接受一個輸入參數,且返回型態在這裡必須是 double:
double & operator [](int i) const;
幸運的是C++及其他較新的程式語言如Java、Python、Scala等,均提供表達與處理例外狀況的機制(exception handling mechanism)。如果將超限視為一種例外狀況,則 operator []可檢查(detect)是否超限,如是則拋出例外(throw an exception),表示超限已發生, 並終止正常函數執行:
double & vector::operator [](int i) const {
if (i <=0 || i > _dim )
throw std::string("Index to vector out of bound!");
return _v[i-1];
}
C++ 提供一個exception class,用來表達例外。我們可以利用這個型態,但是代價為較為複雜些。幸好,C++容許拋出任意型態的例外,例如int、const char* (C string)、string等。這裡,vector::operator[]可拋出的例外型態為string。
呼叫operator []的敘述,則可放置在 try 敘述 (try statement)的try block之中,並由緊跟在後的catch block加以捕捉:
try {
double component = u[0];
}
catch (string s) {
CHECK(string("Index to vector out of bound!") == s);
}
注意 catch敘述是以型態比對的方式捕捉例外。
如超限例外未被捕捉(例如,呼叫operator []的敘述未被放置於try statement或無對應的catch block加以捕捉),則該例外將繼續上傳至前一個做出呼叫的的函數,依此類推,直到到達main。如main未加以捕捉,則C++ runtime將會終結程式。
D步驟:
我們將以string作為operator []超限例外。工作列出如下
T31:寫vector::operator []超限測試。
T32:寫vector::operator []超限檢查,及拋出例外。
T33:決定如何處理vector::operator []超限例外,做必要的程式修改。
T34:寫matrix:at超限測試。
T35:寫matrix:at超限檢查,及拋出例外。
T36:決定如何處理matrix:at超限例外,做必要的程式修改。
TEST (index_outofbound_low, vector){
vector u(2);
try {
double ccomponent = u[0];
}
catch (string s) {
CHECK(string("Index to vector out of bound!") == s);
}
}
TEST (index_outofbound_high, vector){
vector u(2);
try {
double ccomponent = u[3];
}
catch (string s) {
CHECK(string("Index to vector out of bound!") == s);
}
}
double & vector::operator [](int i) const {
if (i <=0 || i > _dim )
throw std::string("Index to vector out of bound!");
return _v[i-1];
}
T33:決定如何處理超限例外,做必要的程式修改。
operator []變更,影響innerProduct.exe 與 linearTransform.exe。我們需要決定當vector index超限發生時,該如何處理?
首先,我們先問,使用者會不會引發超限例外?如果會,則超限例外處理的方式,需要顯考慮使用者。
以innerProduct.exe為例,程式並不會讓使用者有直接引發這個例外的機會。
接著我們問,使用vector::operator []的programmer會不會所引發超限例外?注意,這裡programmer可能是你,或者其他使用你寫的vector class的programmer。同時,這個超限例外極容易發生:由於後來從1開始的使用方法顯然與operator [] 的從0開始慣用法不合,發生超限的機率就更大了。
Programmer引發這個超限例外時,我們可以說這個例外使因為程式的bug造成的。有bug的程式不該給客戶用。所以,這個決定很容易做:提示programmer這個情形後終結程式,讓programmer修復造成超限的code。
決定了超限例外處理方針之後,下一個決定是由哪一個函數來提示programmer這個情形後終結程式。被賦予責任處理這個例外的函數,它的code將因增加try statement而變複雜。根據前面關於例外處理的描述,最簡單的作法是不要做任何事:
超限例外發生時,任由C++ runtime 異常終止 main函數。
以下在main中使用者輸入第一個向量之後,故意讓超限發生,程式執行不正常終止的畫面:
我寫的程式提供你參考。畫面中告訴我們程式在拋出 'std::string' 之後異常終結,並請我們連繫開發者。不論對使用者或programmer,以此方式處置一個bug,應屬恰當。
T34、T35、T36的工作請你試試看。
L步驟:
T31:寫vector::operator []超限測試。
T32:寫vector::operator []超限檢查,及拋出例外。
T33:決定如何處理vector::operator []超限例外,做必要的程式修改。
T34:寫matrix:at超限測試。
T35:寫matrix:at超限檢查,及拋出例外。
T36:決定如何處理matrix:at超限例外,做必要的程式修改。
C步驟:
T31:寫operator []超限測試。
增加兩個超限測試,期望超限發生時,拋出的例外物件為string("Index to vector out of bound!")。
vector u(2);
try {
double ccomponent = u[0];
}
catch (string s) {
CHECK(string("Index to vector out of bound!") == s);
}
}
TEST (index_outofbound_high, vector){
vector u(2);
try {
double ccomponent = u[3];
}
catch (string s) {
CHECK(string("Index to vector out of bound!") == s);
}
}
T32:寫operator []超限檢查,及拋出例外。
if (i <=0 || i > _dim )
throw std::string("Index to vector out of bound!");
return _v[i-1];
}
注意到 throw std::string(...)了嗎?如果只寫 throw string("...")則發生compilation error,原因是vector有一個同名的函數 string(),故以std::string(...)加以區別。另一種做法是將vector::string()改名,例如改為vector::str()。
operator []變更,影響innerProduct.exe 與 linearTransform.exe。我們需要決定當vector index超限發生時,該如何處理?
首先,我們先問,使用者會不會引發超限例外?如果會,則超限例外處理的方式,需要顯考慮使用者。
以innerProduct.exe為例,程式並不會讓使用者有直接引發這個例外的機會。
接著我們問,使用vector::operator []的programmer會不會所引發超限例外?注意,這裡programmer可能是你,或者其他使用你寫的vector class的programmer。同時,這個超限例外極容易發生:由於後來從1開始的使用方法顯然與operator [] 的從0開始慣用法不合,發生超限的機率就更大了。
Programmer引發這個超限例外時,我們可以說這個例外使因為程式的bug造成的。有bug的程式不該給客戶用。所以,這個決定很容易做:提示programmer這個情形後終結程式,讓programmer修復造成超限的code。
決定了超限例外處理方針之後,下一個決定是由哪一個函數來提示programmer這個情形後終結程式。被賦予責任處理這個例外的函數,它的code將因增加try statement而變複雜。根據前面關於例外處理的描述,最簡單的作法是不要做任何事:
超限例外發生時,任由C++ runtime 異常終止 main函數。
以下在main中使用者輸入第一個向量之後,故意讓超限發生,程式執行不正常終止的畫面:
我寫的程式提供你參考。畫面中告訴我們程式在拋出 'std::string' 之後異常終結,並請我們連繫開發者。不論對使用者或programmer,以此方式處置一個bug,應屬恰當。
T34、T35、T36的工作請你試試看。
L步驟:
1. vector::operator [] index 由1而非0開始,有違慣例,對programmer而言,vector::operator []的使用已成bug的溫床。請將vector::operator []改名,例如vector::component(int i)或vector::at(int i)。請你試試看。
2. 前述超限例外,如果我們讓使用者輸入特定維度,為它取得向量在該維度分量,則使用者將有直接引發這個例外的機會,如果這種情形發生,程式應適度提示使用者,使他有機會更正此項錯誤,並繼續正常執行。同理,innerProduct程式有哪些地方會有此類使用者引發的錯誤?這些地方都可以改以例外的方式處理。
© Y C Cheng, 2013. All rights reserved.