異常
條款9:利用destructors避免泄露資源
- 只要堅持這個規則,把資源封裝在對象內(相似智能指針shared_ptr),一般即可以在exceptions出現時避免泄露資源。
- 簡單來講就是,當有資源可能在函數拋異常時而沒法釋放,這時能夠將資源封裝到對象內(RAII),利用對象的析構函數來自動釋放資源,這樣即便有exceptions發生,也不會有資源泄露。
條款10:在constructors內阻止資源泄露(resource leak)
- C++只會析構已構造完成的對象。對象只有在其constructor執行完畢纔算是完成構造穩當。
- 因爲C++不自動清理那些「構造期間拋出exceptions」的對象,因此你必須設計你的constructors,使它們在那種狀況下亦能自我清理。一般這隻須要將全部可能的exceptions捕捉起來,執行某種清理工做,而後從新拋出exception,使它繼續傳播出去便可。
- 可是,更優雅的作法其實是將這些須要在constructor內初始化的對象視爲資源,將它們交由智能指針來管理。
- 結論是:若是你以auto_ptr(C++11後使用shared_ptr或者unique_ptr)對象來取代pointer class members,你便對你的constructors作了強化工事,免除了「exceptions出現時發生資源泄露」的危機,再也不須要在destructors內親自動手釋放資源,並容許const member pointers得以和non-const member pointers有着同樣優雅的處理方式。
條款11:禁止異常(exceptions)流出destructors以外
Session::Session()
{
try {
logDestruction(this);
}
catch (...) {
}
}
- 這裏的catch語句塊看起來什麼都沒作,可是外表容易騙人。這個語句塊阻止了「logDestruction所拋出的exceptions」傳出Session destructor以外。
- 有兩個好理由支持咱們「全力阻止exceptions傳出destructors以外」。第一,它能夠避免terminate函數在exception傳播過程的棧展開(stack-unwinding)機制中被調用;第二,它能夠協助確保destructors完成其應該完成的全部事情。
條款12:瞭解「拋出一個exception」與「傳遞一個參數」或「調用一個虛函數」之間的差別
- 「拋出exception」與「傳遞參數」的相同點是:它們的傳遞方式有3中:by value(傳值),by reference(傳引用),by pointer(傳指針)。然而視你所傳遞的是參數或exceptions,發生的事情可能徹底不一樣。緣由是當你調用一個函數,控制權最終會回到調用端(除非函數失敗以致於沒法返回),可是當你拋出一個exception,控制權不會再回到拋出端。
- 「拋出exception」與「傳遞參數」的區別一:C++特別聲明,一個對象被拋出做爲exception時,老是會發生複製(copy)。即便catch語句參數時by reference,或者拋出對象聲明爲static也同樣會發生複製。且複製動做永遠是以對象的靜態類型爲本。
- 「exception objects一定會形成複製行爲」這一事實也解釋了「傳遞參數」和「拋出exception」之間的另外一個不一樣:後者經常比前者慢。
- 通常而言,你必須使用一下語句:
throw;
才能從新拋出當前的exception,其間沒有機會讓你改變被傳播的exception的類型。此外,它也比較有效率,由於不須要產生新的exception object。架構
- 「拋出exception」與「傳遞參數」的區別二:函數調用過程當中將一個臨時對象傳遞給一個non-const reference參數時不容許的,但對exception則屬合法。
- 「拋出exception」與「傳遞參數」的區別三:通常而言,調用函數傳遞參數過程當中容許的隱式轉換在「exceptions與catch子句相匹配」的過程當中使不會發生的。
- 「exceptions與catch子句相匹配」的過程當中,僅有兩種轉換能夠發生。第一種是「繼承架構中的類轉換」。第二種是從一個「有型指針」轉爲「無型指針」(因此一個參數爲const void*的catch子句,可捕捉任何指針類型的exception)。
- 「拋出exception」與「傳遞參數」的區別四:catch子句老是依出現順序作匹配嘗試。與虛函數調用比較,虛函數採用」best fit「(最佳吻合)策略,而exception處理機制採用」first fit「(最早吻合)策略。所以,絕對不要將」針對base class而設計的catch子句「放在」針對derived class而設計的catch子句「以前。
條款13:以by reference方式捕捉exceptions
- 相比於by reference方式,以by value方式捕獲exception,會使被傳遞的對象發生兩次複製,產生兩個副本。其中一個構造動做用於「任何exceptions都會產生的臨時對象」身上,另外一個構造動做用於「將臨時對象複製到catch的參數上」。
- 千萬不要拋出一個指向局部對象的指針,由於該局部對象會在exception傳離其scope時被銷燬,所以catch子句會得到一個指向「已被銷燬的對象」的指針。
- 若是catch by reference,你就能夠避開對象刪除問題(by pointer會面對的)——它會讓你動輒得咎,作也不是,不作也不是;你也能夠避開exception objects的切割問題(派生類對象的exception objects被捕捉並被視爲基類對象的exception objects,將失去其派生成分。對象切割問題是因爲靜態編聯致使的,使用引用和指針時不會發生);你能夠保留捕捉C++標準exceptions的能力;你也約束了exception objects需被複制的次數。
條款14:明智運用exception specifications
- exception specification示例,一個只拋出int類型exceptions的函數聲明爲:
void fun() throw(int);
- 若是函數拋出一個並未列入exception specification的exception,這個錯誤會在運行時期被檢驗出來,因而特殊函數unexpected會被自動調用。unexpected的默認行爲是調用terminate。
想要避免這種unexpected的方法就是:函數
- 不該該將templates和exception specifications混合使用。
- 若是A函數內調用了B函數,而B函數無exception specifications,那麼A函數自己也不要設定exception specifications。
- 處理「系統」可能拋出的exceptions(如bad_alloc)。
- C++容許你以不一樣類型的exceptions取代非預期的exceptions。若是非預期函數的替代者從新拋出當前的exception,該exception會被標準類型bad_exception取而代之。
void convertUnexpected()
{
throw;
}
set_unexpected(convertUnexpected);
- 若是你作了上述安排,而且每個exception specifications都含有bad_exception,或者其基類,你就不再必擔憂程序會因而非預期的exception時停止執行。
瞭解異常處理(exception handling)的成本
- 爲了讓exception的相關成本最小化,只要可以不支持exceptions,編譯器便不支持;請將你對try語句塊和exception specifications的使用限制於非用不可的地點,而且在真正異常的狀況下才拋出exceptions。