異常處理在C++中的地位是很尷尬的,他不被不少公司或者程序員承認,可是基於某些緣由,我的依然以爲異常處理在C++程序中 是很是必要的。ios
通常來講,異常分爲兩大類,一個是拋出異常,另外一個是接受異常而後處理。c++
通常來講,標準庫中類或者函數會在使用錯誤的時候拋出一些異常,這其實也被人詬病的,畢竟錯誤的使用庫是程序員的責任,標準庫不該該爲此承擔。舉一個string的栗子:程序員
try{ std::string wrolen("234"); std::string it = wrolen.substr(10); }catch(std::exception& e){ std::cout << "Something Wrong:"<<e.what() <<"\n"; }
這個例子將只有3個字符的wrolen,取其10個字符長度的子串,天然會發生錯誤。
結果爲
what(): basic_string::substr: __pos (which is 10) > this->size() (which is 3)
網絡
使用try--catch後,程序不會馬上奔潰,而是執行打印語句,這說明在try語句中有string的異常被拋出。而咱們剛有接住了這個異常。函數
咱們來看完整的測試代碼測試
#include <iostream> #include <string> void doExceptionCatch() { try{ std::string wrolen("234"); std::string it = wrolen.substr(10); }catch(std::exception &e){ std::cout << "Something Wrong:"<<e.what() <<"\n"; } } int main(int argc, char const *argv[]) { try{ doExceptionCatch(); } catch(std::out_of_range &e){ std::cout << "In main handle Something Wrong:"<<e.what() <<"\n"; } return 0; }
測試結果
Something Wrong:basic_string::substr: __pos (which is 10) > this->size() (which is 3)
優化
在doExceptionCatch函數中使用了catch後,原本的異常已經被處理了,所以,回到main函數後,沒有catch到任何異常,故沒有打印。this
一個特地的改動是,在doExceptionCatch函數的catch中加一個throw關鍵字,因而這個異常會被繼續上拋,被main捕獲。編碼
事實上,標準庫中定義了不少種異常,實際編碼中,你能夠在你認爲會產生異常的地方將它們拋出來。固然了,必定是確實產生了異常。code
看下面的例子:
int returnInt(unsigned int val) { return val > 2147483647 ? throw std::out_of_range("too big for a interger!"):val; } int main(int argc, char const *argv[]) { try{ int intme = returnInt(2147483648); std::cout << "In main intme :"<<intme <<"\n"; }catch(std::out_of_range &e){ std::cout << "In main handle Something Wrong:"<<e.what() <<"\n"; } return 0; }
咱們知道一個int的最大數值是2147483647,所以當用戶輸入的數比這個還大,那麼咱們則拋出一個超出範圍的異常。這合情合理。當函數returnInt沒有拋出異常時,程序將正常執行,打印輸入的數字,不然不會執行打印,而是直接進入到catch語句塊。實現了相似(事實上也是)goto語句的功能。
標準庫固然不會具體到項目的方方面面,可能你還須要自行定義一些異常狀況,好比網絡中斷的處理,文件沒法打開的處理等,那麼你就能夠本身定義異常了。通常來講,咱們會繼承標準庫的std::exception來自定義異常類。
class MyException:public std::exception { public: //重載標準exception的what函數 const char* what() const noexcept{ return "Error for my Diy exception!"; } }; void throwMyexception() noexcept(false)//noexcept(false)代表該函數能夠拋出異常,throw(MyException),在C++11中已經廢棄了這種寫法 { //這裏模擬發生的異常,而後拋出自定義異常 if(!false){ throw MyException(); } } int main(int argc, char const *argv[]) { try{ throwMyexception(); }catch(MyException & e){ std::cout << "my own exception is : "<< e.what() <<"\n"; } return 0; }
值得一提的是noexcept 關鍵字,該關鍵字在C++11中引入,用於標識是否會拋出異常。
上面的例子中,已經涉及到如何捕獲異常了,這裏作個說明,不論是標準異常仍是自定義的 異常,只要是可能存在異常拋出的位置,咱們均可以使用try---catch語句塊取處理異常。
異常處理不是神丹妙藥,它不能也不該該做爲你的軟件出錯後的救命稻草(若是軟件要奔潰,就該讓它早點奔潰),它應該做爲軟件在不應發生某些問題下的補充處理,雖然與此同時,這樣的補充會引起代碼膨脹,甚至於濫用。
在《程序員的修煉》一書中,做者對異常的使用時機是這樣認爲的,若是去掉異常處理的部分,程序和以前表現一致,則說明異常處理是正確的引入了,不然,若是一個本來異常的代碼,在異常處理後變正常了,則說明異常處理作了不應作的事。
故此,個人理解是,異常處理不該該試圖修復異常自己,而是應該將問題儘早暴露而後即時的對此作出處理,這個處理不是修復,修復應該是正常代碼該作的事。好比本文第一個例子中,代碼錯誤的取用了超出字符串長度的子串,這裏就不應使用異常處理(好吧,我給本身打臉了),更好的方案是先判斷字符串的長度,而後再取子串。第二個例子,用戶(調用者)錯誤的輸入了大於最大int型的數值,那麼可使用異常,異常處理裏毫不是要把這個輸入值變得小一些,使之合法,而是忽視用戶的調用輸入,並給予提示。那麼爲何不使用if語句直接判斷輸入值是否大於最大int呢?這就涉及到異常的代碼段的集中處理的優點了,它能讓代碼看起來更緊湊,否則,若是這裏的判斷多了,if就該滿天飛了。
這篇文章對異常的好處作了很詳細的詮釋
話分兩頭,不使用異常也是能夠的,可是觀察C++的語言發展,標準會更傾向於加入並優化異常的處理。除了極端的,對速度有超高要求的場景外,咱們須要引入異常,以免程序在毫無防備的地方奔潰退出,讓全部的異常都獲得處理,就能夠將軟件的健壯性提高,若是你的代碼寫得很爛,那就更該引入異常處理,這樣的話,奔潰時,程序至少能「死有所歸」。