1. 程式人生 > 實用技巧 >27,二階構造模式(建構函式二階構造)

27,二階構造模式(建構函式二階構造)

模式------------即方法

1.建構函式的回顧

(1)關於建構函式

  ①類的建構函式用於物件的初始化

  ②建構函式類同名並且沒有返回值(思考:無返回值如何判斷建構函式的執行結果?)

  ③建構函式物件定義自動被呼叫

問題:

1,如何判斷建構函式的執行結果? ---普通函式可以返回值

2,建構函式中執行return會發生什麼?------普通函式立即返回,後面的函式不執行

3,建構函式執行結束是否意味著物件構造成功?-------

程式設計實驗以上問題:

 1 #include<stdio.h> 
 2 /*   1,沒有辦法判斷建構函式的執行結果
 3         2,建構函式中執行return會發生什麼?建構函式執行之後,不意味著物件構造成功
4 */ 5 // 模式即方法 6 class Test 7 { 8 int mi; 9 int mj; 10 11 public: 12 13 Test(int i, int j) //1,建構函式中執行return會發生什麼? 14 { //答:直接返回,Test t1(1,2)有問題,初始狀態有問題,沒有影響建構函式的誕生,不能知道t1能不能用 15 //所以沒有辦法判斷建構函式的執行結果 16 mi = i; 17 18
return; 19 20 mj = j; //這行程式碼沒有執行--------mj變為隨機數,mj成員未完成初始化 21 } 22 23 int getI() 24 { 25 return mi; 26 } 27 28 int getJ() 29 { 30 return mj; 31 } 32 }; 33 34 int main() 35 { 36 Test t1(1,2); 37 38 printf("
t1.mi=%d\n",t1.getI()); //1 39 printf("t1.mj=%d\n", t1.getJ()); //5689365 40 41 return 0; 42 }

回答:建構函式的真相

  ①建構函式只提供自動初始化成員變數的機會,但不能保證初始化邏輯一定成功它只能決定物件的初始狀態,而不是物件的誕生

  ②建構函式執行return語句後,建構函式執行結束,但是不意味著物件構造成功。

那麼怎麼解決?????

 1 #include<stdio.h> 
 2 /*  
 3     判斷物件構造是否成功------方案:類中增加成員-mStatus-----強行讓建構函式有個返回值mStatus
 4     */
 5 //           模式即方法
 6 class Test
 7 {
 8     int mi;
 9     int mj;
10 
11     bool mStatus; 
12 
13 public:
14     Test(int i, int j)  : mStatus(false)   //類的成員變數mStatus的初始化
15     {
16         mi = i;
17 
18         return;                              //mj變為隨機數,mj成員未完成初始化
19 
20         mj = j;
21 
22         mStatus = true;                 //物件構造成功,mStatus返回true
23     }
24     int getI()
25     {
26         return mi;
27     }
28     int getJ()
29     {
30         return mj;
31     }
32 
33     int status()  //功能函式
34     {
35         return mStatus;
36     }
37 };
38 
39 int main()
40 {
41     Test t1(1, 2);
42 
43     if (t1.status())//構造未完成,status為false
44     {
45         printf("t1.mi=%d\n", t1.getI());//i被正確初始化
46         printf("t1.mj=%d\n", t1.getJ());//j為隨機值
47     }
48     return 0;
49 }

2.半成品物件

(1)初始化操作不能按照預期完成得到的物件

(2)半成品物件合法C++物件也是bug的重要來源

記憶體申請不成功,產生段錯誤,但是有的錯誤只產生一次,該怎麼解決這種問題???

人為的將構造分為兩步

3.二階構造---(用於杜絕半成品物件)

(1)工程開發中的構造過程

  ①資源無關的初始化操作階段

      ----------不會出現異常情況的操作(比如將成員變數的值設定為初始值)

  ②需要使用系統資源的操作      

      -------可能出現異常,如記憶體申請(堆空間),訪問檔案等。

二階構造,物件只在堆空間產生,不能在棧上產生,往往實際工程物件是非常大的,不適合放在棧空間

     

為什麼要用物件建立函式???

 因為建構函式是Private外界不能呼叫,所以要通過物件建立函式(static)

例項:

 1 #include<stdio.h> 
 2 
 3 
 4 
 5 class TwoPhaseCons
 6 {
 7 private:
 8     TwoPhaseCons()        //第一階段構造函(簡單的賦值操作)            c++建構函式可以擔任二階構造的一階建構函式
 9     {
10     }
11     bool construct()      //第二階段建構函式(系統資源申請,開啟檔案,網路)                普通成員函式
12     {
13         return true;
14     }
15 public:
16                                             //建構函式時Private外界不能呼叫,通過物件建立函式
17    static TwoPhaseCons* NewInstance();      //物件建立函式 -----------返回物件指標
18                                                                                                   
19 };
20 
21 //物件建立函式實現
22 TwoPhaseCons* TwoPhaseCons::NewInstance()           
23 {
24     TwoPhaseCons* ret = new TwoPhaseCons();         //堆空間建立物件,呼叫建構函式----執行第一階段構造--(靜態成員函式內部可以訪問類的私有成員)
25                                                                                                
26      
27     //r若第二階段構造失敗,返回NULL
28     if (!(ret && ret->construct()))             //construct----進行資源申請-------執行二階構造
29     {
30         delete ret;                            //刪除半成品物件
31         ret = NULL;
32     }
33 
34     return ret;                           //返回物件,資源申請成功-----合法物件NewInstance()申請成功
35 }
36 
37 int main() 
38 {
39    // TwoPhaseCons obj;                           //error  
40 
41   //  TwoPhaseCons* obj =new NewInstance();       //error 觸發建構函式自動呼叫,但是建構函式是私有的,不能在外部main()自動呼叫,只能呼叫建立函式
42 
43      TwoPhaseCons* obj= TwoPhaseCons::NewInstance(); //只能呼叫靜態建立函式
44 
45     printf("obj=%p\n",obj);                         //可以得到合法可用的物件----位於堆空間
46 
47     return 0;
48 }

【程式設計實驗】陣列類的加強 IntArray

//IntArray.h

 1 #ifndef  _INTARRAY_H
 2 #define  _INTARRAY_H
 3 
 4 class IntArray
 5 {
 6 private:
 7     int m_length;                           //陣列長度
 8     int* m_pointer;                         //陣列資料
 9 
10     IntArray(int len);                       //一階構造
11     IntArray(const IntArray& obj);             //一階構造        
12     bool construct();                             //實現第二階段的構造
13 
14 public:
15     static IntArray* NewInstance(int length);     //物件建立函式
16 
17     int length();                                  //獲取陣列長度                                            
18     bool get(int index, int& value);              //獲取對應位置的陣列元素值
19     bool set(int index, int value);               //設定對應位置的陣列元素為引數value
20     void free();
21 };
22 
23 #endif
24  

////IntArray.cpp

 1 #include"IntArray.h"
 2 
 3 //第一階段存放不存在錯誤的操作
 4 //可能失敗的操作放在第二階段
 5 
 6 IntArray :: IntArray(int len)         //作用域分辨符::
 7 {
 8     m_length = len;
 9 }
10 
11 bool IntArray::construct()             //建構函式
12 {
13     bool ret = true;
14      
15     m_pointer = new int[m_length];      //資料指標指向堆空間裡的一段記憶體:陣列作用,儲存整形陣列
16     
17     if (m_pointer)                        // 要判斷記憶體是否申請成功
18      {
19         for (int i = 0; i < m_length; i++)    //將陣列所有值設定為0
20         {
21             m_pointer[i] = 0;
22         }
23     }
24     else
25     {
26         ret = false;
27     }
28     return ret;
29 }
30 
31 int IntArray :: length()
32 {
33     return m_length;
34 }
35 
36 bool IntArray :: get(int index, int& value)    //安全性體現----得到對應位置的陣列元素值 ---index為指定位置
37 {
38     bool ret = (0 <= index) && (index <= length());     
39 
40     if (ret)
41     {
42         value = m_pointer[index];              //通過引用返回值
43     } 
44 
45     return ret;
46 }
47 
48 IntArray* IntArray::NewInstance(int length)       ///物件建立函式
49 {
50     IntArray* ret = new IntArray(length);
51    
52     //r若第二階段構造失敗,返回NULL
53     if (!(ret && ret->construct()))
54     {
55         delete ret;
56         ret = 0;
57     }
58 
59     return ret;
60 }
61 
62 
63 bool IntArray :: set(int index, int value)             //設定對應位置的陣列元素為引數value
64 {
65     bool ret = (0 <= index) && (index <= length());
66     if (ret)
67     {
68         m_pointer[index]=value;                      //陣列成員重新賦值
69     }
70     return ret;
71 }
72 
73 void IntArray::free()
74 {
75     delete[] m_pointer;       //釋放堆空間
76 }

//main.c

 1 #include<stdio.h>
 2 #include "IntArray.h"
 3 
 4 
 5 int main() {
 6     
 7         
 8 //呼叫建立函式
 9     IntArray* a = IntArray::NewInstance(5);
10     printf("a.length=%d\n",a->length());
11 
12     a->set(0, 1);
13 
14     for (int i = 0; i <a->length() ; i++)                                          
15     {
16         int v = 0;
17 
18         a->get(i, v);
19 
20         printf("a[%d]=%d\n",i,v);
21     }
22 
23 
24     return 0;
25 }

4.小結

(1)建構函式只能決定物件初始化狀態

(2)建構函式初始化操作的失敗不影響物件的誕生

(3)初始化不完全半成品物件bug的重要來源

(4)二階構造人為的將初始化過程分為兩個部分

(5)二階構造能夠確保建立的物件都是完整初始化的