国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

C++的錯誤和異常處理分析

2019-11-17 05:28:11
字體:
來源:轉載
供稿:網友
何時使用異常?

  一個簡單的回答是:“當異常的語義和性能要求都恰當的時候?!?br />
  一個經常被提到的方法是這樣問自己:“這是一個例外(或者意外的)情形嗎?”這個方法貌似挺吸引人,但是通常只會導致錯誤答案。對一個人來說是“異?!钡那樾螌α硪粋€人卻“正?!保寒斈阏嬲屑毧紤]這句話時,就發現無法作出區分,這句話根本幫不了你。究竟,假如你檢查了某個錯誤條件,就意味著你認為它會發生,否則你的檢查不過是垃圾代碼。

  一個更合適的問法是:“這里需要棧展開嗎?”由于異常處理實際上幾乎都意味著比正常流程代碼要慢,還應該問自己:“這里負擔得起棧展開的代價嗎?”比如,正在做的一個要花很長時間的計算,并且周期性地檢測用戶是否按下了取消鍵。拋出異??梢詢炑诺厝∠僮?。另一方面,在這個計算的內部循環中拋出并捕捉處理異常可能就不恰當,這么做可能導致嚴重的性能下降。前述內容包含這樣一個原則:對于時間要害的代碼,拋出異常才是一種“異常”的做法,而不是常規.

  如何設計異常類?

  1. 從std::exception派生異常類。除了一些非常罕見的情況,例如負擔不了需函數的開銷。把std::exception作為異?;愂呛侠淼模斔粡V泛使用后,將答應程序員捕捉任何異常而不必使用catch(...).更多關于catch(...)的內容,請看后文。

  2. 使用虛擬繼續。這個深刻的洞察力來自Andrew Koenig. 當拋出的一個異常是從多個基類派生,并且這些基類有共同的部分,catch點就會碰到歧義問題,從異常基類虛擬繼續可以防止這種歧義問題:

#include <iostream>
strUCt my_exc1 : std::exception { char const* what() const throw(); };
struct my_exc2 : std::exception { char const* what() const throw(); };
struct your_exc3 : my_exc1, my_exc2 {};

int main()
{
try { throw your_exc3(); }
catch(std::exception const& e) {}
catch(...) { std::cout << "whoops!" << std::endl; }
}
  上面的程序將打印出“whoops” ,因為C++運行時刻無法決定用那個exception實例去匹配第一個catch.(禿子:我的建議是這里最好別使用多重繼續)

  3. 不要內嵌std::string對象或者其他拷貝構造可能拋出異常的數據成員、基類。在上述點拋出異常將導致直接調用std::terminate().讓基類或數據成員的默認構造函數可能拋出異常也是同樣糟糕的主意,你本來是打算通過一個包含對象構造的throw表達式報告異常, 程序卻無謂地中止了:

throw some_exception();

  當發生異??截悤r,有幾種方法避免復制字符串對象,例如在異常對象中嵌入一個定長存儲區,或者通過引用計數來治理字符串。不過,在采用這些方法前,先考慮考慮下一條。

  4. 只在確實需要的時候才格式化what()返回的信息。格式化是一個典型的內存相關的操作,有可能拋出異常。最好把格式化推遲到棧展開之后,因為棧展開可能釋放某些資源。對what()函數用catch(...)塊加以保護是一個好主意,這樣你就可以在格式化拋出異常時有了一個退路。

  5. 不要太在意what()的信息。在異常拋出點,對程序員來說,這是給出錯誤信息的好機會,但是你未必能夠把相關信息組合成用戶可以理解的形式。國際化就一個典型的情況。Peter Dimov給出了良好建議:建一個錯誤信息格式化的表格,把what()的字符串作為這個表的鍵。當標準庫拋出異常時,假如我們只能獲得其標準的what()字符串……

  6. 在異常類的public接口中暴露導致錯誤的有關信息。返回固定信息的what()意味著你忽視了暴露信息,而用戶可能需要提供相關信息。例如,你的異常想報告數字范圍錯,報錯的代碼應該能夠透過異常的公共接口讓異常包含導致問題的那個變量值。假如你只是在what()中以文本方式表現這些數字,那些需要根據信息做更多(或更少)處理的程序員日子將很難過。

  7. 假如可能,讓你的異常類對兩次析構免疫。幾款流行的編譯器偶然會使異常對象被銷毀兩次。假如你能采取措施防御危害(比如,把釋放的指針置零)就可以使代碼更健壯。

  如何處理程序員犯錯?

  作為開發者,假如我違反了所使用庫的某個前條件,我不希望棧展開。我希望的是core dump或者等價物—一個能精確地在問題發生點檢查程序狀態的方法。這通常意味著assert()或者其他類似的東西。

  有時候為用戶提供可以應付任意誤用的強健的API是有必要的,但這樣通常要付出不菲的代價。比如,一個常見需求是跟蹤客戶使用的每一個對象,從而可以驗證合法性。假如你需要這種保護,通常是在一個簡單API上再封裝一層來實現。盡管你做得小心翼翼,有強健承諾的API也只能防御某些而不是所有會導致災難的誤用??蛻粢查_始依靠那些保護并且所依靠的保護也將增長到接口保護不到的部分。

  windows開發者請注重:當你使用assert()時,大部分Windows編譯器實際上都是拋出異常,并且被本地截獲,這很不幸。事實上,截獲的錯誤經常是段訪問失敗或者除零錯。當你使用JIT(Just In Time)調試時這是個問題,這意味著在在喚醒調試器之前已經異常棧展開了,因為catch(…)將捕捉這個異常,其實這個并非C++異常。幸運的是,有一個鮮為人知的簡單辦法可以處理:

extern "C" void straight_to_debugger(unsigned int, EXCEPTION_POINTERS*)
{
throw;
}
extern "C" void (*old_translator)(unsigned, EXCEPTION_POINTERS*)= _set_se_translator(straight_to_debugger);
  這個方法無法應付在catch塊中(或者catch塊調用的函數中)拋出結構化異常的情況,但它確實可以解決絕大多數JIT導致的問題。

  該如何處理異常?

  壓根就不處理異常一般是處理異常的最好辦法。假如你讓異常穿越你的代碼,并且在析構函數中做清理工作,代碼會更干凈。

  盡可能避免catch(…)

  很不幸,其他非Windows操作系統一樣會把非C++異常(例如線程中止)卷入到C++異常機制中去,而且,有時候也沒有類似上面提到的_set_se_translator這樣的hack手法加以解決。我們通常在析構函數或者catch塊中做合理操作來維持系統的不變式,這通常是安全的。然而catch(...)也會捕捉非預期的系統通知,這時是不可能像對待普通C++異常一樣來處理的,慣用的手法不再安全了。

  經過新聞組上長期的辯論之后,盡管不情愿,我還是得承認Hillel Y. Sims觀點:除非所有操作系統修正前面的問題,否則,所有異常應該繼續自std::exception,當所有人適應catch(std::exception&)而不是catch(...)時,世界將會更加美好。

  即使不考慮和操作系統間糟糕的交互情況,有時候,catch(...)仍然是最合適的選擇。假如你根本不知道會有什么異常拋出,并且必須停止棧展開,這可能是你唯一出路。一個典型的情況就是跨語言的時候。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 重庆市| 海晏县| 湖州市| 漠河县| 陆良县| 岑巩县| 临海市| 长海县| 湖北省| 通辽市| 浦城县| 盐津县| 阆中市| 华亭县| 仲巴县| 靖江市| 涿州市| 宁河县| 林芝县| 秭归县| 元氏县| 浦县| 木兰县| 冕宁县| 翼城县| 新邵县| 子洲县| 文安县| 徐汇区| 金坛市| 南木林县| 米林县| 石首市| 怀柔区| 深水埗区| 湄潭县| 南华县| 惠东县| 米易县| 渝北区| 临颍县|