C++構造函數異常(一)


 

C++ 構造函數的異常是一個比較難纏的問題,很多時候,我們可能不去考慮這些問題,如果被問到,有人可能會說使用RAII管理資源。

但你真的考慮過如果構造函數失敗了,到底會發生什么嗎,前面構造成功的成員、基類對象應該怎樣回收?

最近在知乎上看到有人提到這個問題:

http://www.zhihu.com/question/22889420

看了陳碩的回答,抱着懷疑的心態寫代碼加以驗證。

在此之前,先不急着上代碼,啰嗦幾句話。

 首先問4個問題,這是從 Herb sutter 的《More Exceptrional C++》看到的,我覺得問的很好,類似保安的哲理問題:你是誰、你從哪里來、你要到哪里去?

1:對象生命周期何時開始

一個構造函數成功執行完畢,並成功返回之時,也就是構造函數成功執行到函數體尾,沒有發生異常。

2:對象生命周期何時結束

當一個對像的析構函數開始執行,也就是達到析構函數開始指出,這里暫且不討論析構函數是否發生異常,只要進入析構函數體,該對象生命周期就已經結束

3:在生命周期開始之前,與生命結束之后,對象處於什么狀態

這時候“對象”已不是對象。理論上“它”根本就不存在

4:接着第三個答案,如果構造函數異常,對象處於什么狀態?

構造函數異常,即構造函數甚至沒有到達函數體的尾部,即對象的生命周期還沒有開始,所以他根本不是一個的對象,或者說它什么都不是,

所以更不會執行析構函數了。

 

那么問題來了,如果構造失敗,之前成功分配的資源怎么辦呢?

Herb sutter的答案是:這個是語言本身來負責回收了,編譯器來實現,沒程序員的事,即使之前成功構造的對像也不會執行析構函數。

 

下面是陳碩列舉構造函數失敗可能發生的場景,他舉了5個例子,我這里寫了4個,我的結論如下

 

1:構造函數的初始化列表里拋異常,前面已經構造好的成員由編譯器負責回收,不會調用析構函數

 

2:數組元素構造時拋異常,前面已經構造好的元素由編譯器回收,不會調用對象的析構函數。

 

3:多繼承中某個基類的構造函數拋異常,已經構造成功的基類對象由編譯器回收,不會調用析構函數

 

4:智能指針,STL 容器 存放auto_ptr<T>, shared_ptr<T> 對象, 類型T構造失敗,則前面構成成功的智能對象有編譯器回收,不會調用析構函數。

 

第一種:構造函數初始化列表拋出異常,前面成功構造的對像由編譯器負責回收,不會調用析構函數

 1 #include<iostream>
 2 
 3 using namespace std;
 4 
 5 class B{
 6 
 7 public:
 8     B(){
 9         cout << "construct B default" << endl;
10         throw 3; //故意在默認構造函數中拋出異常
11     }
12     B(int num){
13         age = num;
14         cout << "constructor B ,age =" << num << endl;
15     }    
16     ~B(){
17         cout << "destructor B ,age=" << age << endl;
18     }
19 private:
20     int age;
21 };
22 
23 class A{  
24 public:
25     A():_data(new char[1]), b(B(10)), bp(new B()){
26         cout << "construct A " << endl;
27 
28         *_data = '\0';    
29         
30     }
31     ~A(){
32         cout << "destructor A" << endl;
33         delete [] _data;
34         delete bp;
35 
36     }
37 private:
38     char *_data;
39     B b;
40     B *bp;
41 };
42 
43 int main(void){
44 
45     A a;
46 }

第十行出,我故意throw 3 

所以在25行class A構造函數的初始化列表,調用B(10)發生異常,而B()則會異常退出。結果如下

 

B的默認構造函數和B(int)都執行完畢,但析構函數沒有執行,當然A的構造也失敗了,更不會執行析構函數,這些資源都是編譯器負責回收了

如果你不信的會,我注釋第9行,是另一種結果

 

 第2種:數組元素構造發生異常,前面構造成功的對象由編譯器負責回收,不會調用析構函數

 

 1  1 class C{
 2  2 public:
 3  3     C(){
 4  4         //得到指定范圍[m,n]的隨機數:r = rand()%(n - m + 1) + m;
 5  5         int r = rand()%(8-0)+0;
 6  6         num = r;
 7  7         cout << "constuctor C ,num = " << num << endl;
 8  8         if(!(r % 4)){
 9  9             throw r;
10 10         }
11 11     }
12 12     ~C(){
13 13         cout << "destructor C, num = " << num << endl;
14 14     }
15 15 private:
16 16     int num;
17 17 };
18 18 
19 19 int main(void){
20 20 
21 21 
22 22     C arr[10];
23 23     
24 24 }

當隨機數r 整除 4是,即throw異常,則前面成功構造的對象不會析構

 

若注釋第9行的throw r 結果是:

 

第3、4兩種場景,在第二篇文章驗證:《C++構造函數異常(二)》


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM