05-令人頭大的異常

前言

要正確瞭解異常,必須知道異常到底是什麼東西,這個東西在編程實踐中起到什麼作用。異常情形是指阻止當前方法或作用域繼續執行的問題。你所能做的就是從當前環境跳出,並且把問題提交給上一級環境,這就是拋出異常時所發生的事情。而且很多程序需要長期執行,比如服務器。不能因爲一個網絡連接失敗就將這個程序當掉。應該交給上級環境進行處理。所以今天我們就來了解如何使用這個令人頭大的異常。

沒有異常的世界

很多C++程序員都是從C語言轉過來的,C語言就有一個很大特點就是沒有異常處理機制。最常見的一個例子就是,編寫一個除法函數。

float division(float lhs, float rhs) {
	if(rhs == 0)
		printf("除數不能爲零"):
	else
		return lhs / rhs;
}

我們通常是通過條件判斷來規避異常,但是C++引入了類的概念。類的構造函數不允許返回錯誤,默認聲明爲noexpect。但是有一些函數還是情況還是會出現一些錯誤的,我們通常是使用init()函數來執行構造操作。但是這個時候原來的C語言裏面的條件判斷機制就不再有效了,需要引入新的異常處理機制。

安全機制

對於安全機制通常分爲三級,基本承諾強烈保證不拋擲保證

  • 基本承諾:如果異常被拋出,程序內的任何事物依然保持在有效狀態下。沒有任何對象或數據結構被破壞。
  • 強烈保證:如果函數成功,則成功;如果函數失敗,則程序回滾到調用函數之前的狀態。
  • 不拋擲保證:承諾絕不拋出異常,C++11新增了noexcept關鍵字,noexcept保證了這個函數不會拋出異常,只有終止程序和成功執行兩種狀態。

異常使用

C++ 異常處理涉及到三個關鍵字:try、catch、throw。

  • throw: 當問題出現時,程序會拋出一個異常。這是通過使用 throw 關鍵字來完成的。
  • catch: 在您想要處理問題的地方,通過異常處理程序捕獲異常。catch 關鍵字用於捕獲異常。
  • try: try 塊中的代碼標識將被激活的特定異常。它後面通常跟着一個或多個 catch 塊。
try
{
   // 保護代碼
}catch( ExceptionName e1 )
{
   // catch 塊
}catch( ExceptionName e2 )
{
   // catch 塊
}catch( ExceptionName eN )
{
   // catch 塊
}

C++標準異常
在這裏插入圖片描述
每種異常的具體情況可以查閱菜鳥教程

異常的弊病

如果異常是個完美的東西,那麼谷歌也不會放棄使用異常機制了。

  1. 異常違反了"你不用就不需要付出代價"的原則,只要開啓異常,即使不使用,也會使二進制代碼膨脹;
  2. 異常比較隱蔽,不容易看出來那些地方會發生異常和發生什麼異常。

尤其是第二條,一旦一個函數聲明不會拋出異常,結果拋出了異常,C++運行時會使用std::terminate終止程序。

完美使用異常例子

在std容器中,vector在緩衝區充滿之後使用拷貝構造函數初始化新的區域,而不是採用移動構造函數。這裏麪包含了強烈保證的思想。
因爲在移動構造函數中,假設有16個元素需要移動。現在已經移動了12個元素,在移動第13個元素的時候,發生異常出錯。由於vector滿足強烈保證的安全機制,需要將回滾到函數執行前的狀態。但是如果使用移動構造函數,原來位置的元素已經被移走。沒辦法恢復了。所以爲了滿足這個安全機制,寧願捨棄一部分效率,來提升安全性。

Q&A

  1. Q: 如何理解下面這句話:"首先是內存分配。如果 new 出錯,按照 C++ 的規則,一般會得到異常 bad_alloc,對象的構造也就失敗了。這種情況下,在 catch 捕捉到這個異常之前,所有的棧上對象會全部被析構,資源全部被自動清理。"new是在堆上分配內存的錯誤,但是棧上的內存爲什麼被析構?
    A: 堆上的東西都是由棧上變量所引起的,棧上的對象析構了,堆上的資源也就不存在了。這就是最常用的RAII方法。

總結

這一部分內容我也是剛學,上面這些總結只是我的一點拙見。希望大家能夠給我指正!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章