異常基本概念ios
異常即程序在運行期間產生的非預期性錯誤,如內存配額不足、文件不存在、網絡不可用、SQL語句非法等。express
異常分爲可恢復性異常和不可恢復性異常。網絡
異常是否可恢復,不一樣的應用場景將有不一樣的理解。例如,對於內存配合不足,在短暫的峯涌狀況下內存可以快速恢復則屬於可恢復性異常,由於一但被釋放和回收,程序將回到常態;若內存長時間得不到釋放,則屬於不可恢復性異常,此時應當終止程序的運行。函數
異常又可分爲已知異常和未知異常。已知異常是指程序編寫者事先考慮到的異常,對異常有清晰的定義和處理邏輯,並在程序中進行捕捉;未知異常是指程序編寫者事先未察覺到的異常,能夠理解爲程序bug。this
異常捕獲是一把雙刃劍,對異常進行顯示捕獲,尤爲對於bug型的未知異常,須要找出產生異常的緣由和代碼進行修復,即須要考慮異常的分析定位機制。所以,對於未知異常或者沒法處理的異常,不該當使用catch(…)籠統的進行捕獲,當不須要關心異常或者沒法處理異常的時候,就應該避免捕獲異常或者將未處理的異常再次拋出,以便交由bugreport統一對未處理異常進行捕獲、分析。spa
函數的異常聲明指針
在函數的聲明後面使用throw關鍵字可對函數的異常產生進行限制。code
不容許函數拋出任何異常對象
void f() throw(); 表示f函數不容許拋出任何異常;以VS2005編譯器爲例,若在f函數中拋出異常,編譯時會有以下提示:內存
warning C4297: 'f' : function assumed not to throw an exception but does
1> __declspec(nothrow) or throw() was specified on the function
但異常仍是能正常拋出和被捕獲。
容許函數拋出任意異常
void f() throw(…);表示容許f函數拋出任意異常。
容許函數拋出指定類型的異常
void f() throw(MyException1,MyException2);表示容許函數f拋出MyException1和MyException2兩種類型的異常。以vs2005爲例,在f中拋出其餘類型的異常,編譯未見提示,且異常能正常拋出和被捕獲。
異常的拋出
使用關鍵字throw拋出異常,有以下兩種語法。
throw express;拋出express所表明的表達式的異常,異常類型爲表達式的值的類型,異常的值爲表達式的值。例如,
try{ throw 10; }catch(int e){ //progress.... }
throw;將異常繼續「向上傳遞」。例如,
try{ throw 10; }catch(int e){ throw... }
異常類型
任何類型均可以做爲異常類型,包括基本數據類型,如int、double、char等;還包括類類型,如std:string;還包括自定義異常類型,即從std::exception派生的子類。
異常對象的傳遞
自定義的異常類:CCustomException
#pragma once #include <string> using namespace std; class CCustomException : public std::exception { public: CCustomException(void); CCustomException(const char*); ~CCustomException(void); std::string getMsg(); void setMsg(std::string msg); virtual const char* what() const; private: std::string m_msg; };
#include "StdAfx.h" #include "CustomException.h" CCustomException::CCustomException(void) { } CCustomException::~CCustomException(void) { } CCustomException::CCustomException(const char* msg) { m_msg = msg; } std::string CCustomException::getMsg() { return m_msg; } void CCustomException::setMsg(std::string msg) { m_msg = msg; } const char* CCustomException::what() const { return m_msg.c_str(); }
main函數以下:
// ExceptionExample.cpp : 定義控制檯應用程序的入口點。 // #include "stdafx.h" #include "CustomException.h" #include <iostream> void exceptionTest1() throw(CCustomException) { throw CCustomException("this is CCustomException Test1!"); return; } void exceptionTest2() throw(CCustomException*) { throw new CCustomException("this is CCustomException Test2!"); return; } void exceptionTest3() throw(CCustomException&) { throw(CCustomException("this is CCustomException Test3!")); return; } void Test1() { try { exceptionTest1(); } catch (std::exception e) { cout<<e.what()<<endl; } } void Test2() { try { exceptionTest2(); } catch (std::exception *e) { cout<<e->what()<<endl; delete e; } } void Test3() { try { exceptionTest3(); } catch (std::exception &e) { cout<<e.what()<<endl; } } int _tmain(int argc, _TCHAR* argv[]) { Test1(); Test2(); Test3(); getchar(); return 0; }
輸出結果以下:
異常對象的傳遞和函數參數的傳遞同樣,有值傳遞、指針傳遞和引用傳遞3種傳遞方式。
值傳遞會產生臨時對象拷貝,且不支持多態。,雖然CCustomException是std::exception的子類,可是仍然不能調用CCustomException的what函數,輸出了Unknown Message。
指針傳遞
指針傳遞能夠實現多態,但會將臨時對象的地址做爲指針傳出去,出現懸掛指針錯誤。若是在堆上分配內存空間,又不知道什麼時候恰當的刪除對象,二來代碼可讀性、可維護性遭到破壞,違反了內存空間從哪裏申請獲得就在哪裏釋放的內存分配釋放原則。
值引用傳遞既能避免臨時對象的拷貝,又能支持多態,且沒有對象釋放問題。推薦採用值引用的方式傳遞異常對象。輸出結果爲:This is CCustomException Test3.
catch的匹配規則
異常匹配採用自底向上的匹配順序進行匹配,即先在異常產生的函數中進行匹配,若該形成該異常的代碼沒有被try…catch…捕獲或者catch類型不匹配,則向上傳遞直到匹配到第一條catch結束匹配,或則未發現任何匹配的catch則產生未處理異常交由運行時環境處理。
對於基本數據類型的異常,採用嚴格類型匹配機制,不支持類型轉換
對於自定義類型,基類類型可以匹配子類異常對象,子類類型不能匹配基本異常對象。
C++提供了強大的異常處理機制將異常產生和異常處理的代碼分離,即將程序的正常業務邏輯和錯誤處理邏輯進行分離,並在不改變函數原型的狀況下,實現異常對象的「向上」傳遞。
C++的異常處理機制有3部分組成:try(檢查),throw(拋出),catch(捕獲)。把須要檢查的語句放在try模塊中,檢查語句發生錯誤,throw拋出異常,發出錯誤信息,由catch來捕獲異常信息,並加以處理。通常throw拋出的異常要和catch所捕獲的異常類型所匹配。異常處理的通常格式爲:
try { 被檢查語句 throw 異常 } catch(異常類型1) { 進行異常處理的語句1 } catch(異常類型2) { 進行異常處理的語句2 }