天天看點

C++構造函數、析構函數與抛出異常麼?

 從文法上來說,構造函數和析構函數都可以抛出異常。但從邏輯上和風險控制上,構造函數可以,析構函數不推薦抛出異常。

(1)構造函數可以抛出異常

無論何時,從構造函數中抛出異常都是可以的。動态建立對象要進行兩個操作:配置設定記憶體和調用構造函數。若在配置設定記憶體時出錯,會抛出bad_alloc異常;若在調用構造函數初始化時出錯,會不會存在記憶體洩漏呢?答案是不會。

new運算符保證不會出現記憶體洩漏:

T *p = new T;
           

将被編譯器轉換給類似下面的樣子:

  1. void allocate_and_construct()

  2. {

  3. // 第一步,配置設定原始記憶體,若失敗則抛出bad_alloc異常

  4. try

  5. {

  6. // 第二步,調用構造函數構造對象

  7. new (p)T; // placement new: 隻調用T的構造函數

  8. }

  9. catch(...)

  10. {

  11. delete p; // 釋放第一步配置設定的記憶體

  12. throw; // 重抛異常,通知應用程式

  13. }

  14. }

(2)析構函數不推薦抛出異常,如果析構函數可能抛出異常,那麼必須要求在析構函數内消化所有異常或者結束程式。

more effective c++提出兩點理由(析構函數不能抛出異常的理由)

1)如果析構函數抛出異常,則異常點之後的程式不會執行,如果析構函數在異常點之後執行了某些必要的動作比如釋放某些資源,則這些動作不會執行,會造成諸如資源洩漏的問題。 [正常情況下調用析構函數抛出異常導緻資源洩露]

2)通常異常發生時,c++的機制會調用已經構造對象的析構函數來釋放資源,此時若析構函數本身也抛出異常,則前一個異常尚未處理,又有新的異常,會造成程式崩潰的問題。 [在發生異常的情況下調用析構函數抛出異常,會導緻程式崩潰]

 解決方案:

1) 如果某個操作可能會抛出異常,class應提供一個普通函數(而非析構函數),來執行該操作。目的是給客戶一個處理錯誤的機會。

2) 如果析構函數中異常非抛不可,那就用try catch來将異常吞下,必須要把這種可能發生的異常完全封裝在析構函數内部,決不能讓它抛出函數之外。