第19課構造函數(下)

1. 兩個特殊的構造函數
  -無參構造函數
    沒有參數的構造函數
    解釋:無參構造函數看似很是簡單,可是它就特別在它是必須存在的。由於咱們要使用一個類的話,就必須建立對象。建立對象必然牽涉到構造函數的調用。
    注意:當類中沒有定義構造函數時,編譯器默認提供一個無參構造函數,而且函數體爲空。必定要特別注意,當類中沒有構造函數時,編譯器纔會提供,不然不會提供。
  -拷貝構造函數
    參數爲const class_name&的構造函數
     (判斷一個構造函數是否是拷貝構造函數的依據:一、const 二、當前類對象的引用)只要看到這樣的參數出如今一個構造函數裏面,它必然的就是一個拷貝構造函數了。面試

    當類中沒有定義拷貝構造函數時,編譯器默認提供一個拷貝構造函數,簡單的進行成員變量的值複製。數組

 1 #include <stdio.h>
 2 
 3 class Test  4 {  5 private:  6     int i;  7     int j;  8 public:  9     int getI() 10  { 11         return i; 12  } 13     int getJ() 14  { 15         return j; 16  } 17     /*Test(const Test& t) 18  { 19  i = t.i; 20  j = t.j; 21  } 22  Test() 23  { 24  }*/
25 }; 26 
27 int main() 28 { 29  Test t1; 30     Test t2 = t1; 31     
32     printf("t1.i = %d, t1.j = %d\n", t1.getI(), t1.getJ()); 33     printf("t2.i = %d, t2.j = %d\n", t2.getI(), t2.getJ()); 34     
35     return 0; 36 }

在上面這個例子中,一旦定義了網絡

Test(const Test& t)
{
       i = t.i;
       j = t.j;
}
那麼Test t1;就會出錯。爲何?
由於找不到一個Test()的構造函數。
問題:不是編譯器會默認提供一個無參構造函數嗎?
必定要注意,編譯器提供默認構造函數的前提,就是類中沒有構造函數,一旦擁有,編譯器就再也不提供。

呈現一個經典面試題,下面的這個類,它裏面有什麼東西?函數

class Tspa

{code

};對象

它裏面至少有一個無參構造函數,是編譯器提供的。blog

2. 拷貝構造函數的意義內存

-兼容C語言的初始化方式
    例如:int i =2; int j = i;
         Test t1; Test t2 = t1;
-初始化行爲可以符合預期的邏輯資源

-淺拷貝
  拷貝後對象的物理狀態相同
-深拷貝
  拷貝後對象的邏輯狀態相同

編譯器提供的拷貝構造函數只進行淺拷貝。

 1 #include <stdio.h>
 2 
 3 class Test  4 {  5 private:  6     int i;  7     int j;  8     int* p;  9 public: 10     int getI() 11  { 12         return i; 13  } 14     int getJ() 15  { 16         return j; 17  } 18     int* getP() 19  { 20         return p; 21  }30     Test(int v) 31  { 32         i = 1; 33         j = 2; 34         p = new int; 35         
36         *p = v; 37  }42 }; 43 
44 int main() 45 { 46     Test t1(3); 47  Test t2(t1); 48     
49     printf("t1.i = %d, t1.j = %d, *t1.p = %p\n", t1.getI(), t1.getJ(), t1.getP()); 50     printf("t2.i = %d, t2.j = %d, *t2.p = %p\n", t2.getI(), t2.getJ(), t2.getP());54     
55     return 0; 56 }

上面的這個程序,編譯會順利經過。而且你會看到一個現象,t1對象的p和t2對象的p指向了同一段堆空間。你是否是想,這是確定的啊。由於我就是用t1初始化t2,這正是我想要的結果。可是根據經驗,p是在堆上生成的,當咱們不用的時候,須要將其釋放掉。以下面的代碼所示:

 1 #include <stdio.h>
 2 
 3 class Test  4 {  5 private:  6     int i;  7     int j;  8     int* p;  9 public: 10     int getI() 11  { 12         return i; 13  } 14     int getJ() 15  { 16         return j; 17  } 18     int* getP() 19  { 20         return p; 21  }30     Test(int v) 31  { 32         i = 1; 33         j = 2; 34         p = new int; 35         
36         *p = v; 37  } 38     void free() 39 { 40 delete p; 41 } 42 }; 43 
44 int main() 45 { 46     Test t1(3); 47  Test t2(t1); 48     
49     printf("t1.i = %d, t1.j = %d, *t1.p = %p\n", t1.getI(), t1.getJ(), t1.getP()); 50     printf("t2.i = %d, t2.j = %d, *t2.p = %p\n", t2.getI(), t2.getJ(), t2.getP()); 51     
52  t1.free(); 53  t2.free(); 54     
55     return 0; 56 }

編譯就會出錯,由於咱們釋放了兩次相同堆空間中的內存。t1中的p和t2中的p指向了同一段堆空間。

如何解決這個問題?

應該手工來提供一個拷貝構造函數

 1 #include <stdio.h>
 2 
 3 class Test  4 {  5 private:  6     int i;  7     int j;  8     int* p;  9 public: 10     int getI() 11  { 12         return i; 13  } 14     int getJ() 15  { 16         return j; 17  } 18     int* getP() 19  { 20         return p; 21  } 22     Test(const Test& t) 23  { 24 i = t.i; //首先是值的複製 25 j = t.j; 26 p = new int; 27 28 *p = *t.p; //p的值就不能直接複製了,須要從新到堆空間中申請。申請完以後,將t1對象中p空間中的值拿出來,放到新申請的堆空間中去。 29  } 30     Test(int v) 31  { 32         i = 1; 33         j = 2; 34         p = new int; 35         
36         *p = v; 37  } 38     void free() 39  { 40  delete p; 41  } 42 }; 43 
44 int main() 45 { 46     Test t1(3); 47  Test t2(t1); 48     
49     printf("t1.i = %d, t1.j = %d, *t1.p = %p\n", t1.getI(), t1.getJ(), t1.getP()); 50     printf("t2.i = %d, t2.j = %d, *t2.p = %p\n", t2.getI(), t2.getJ(), t2.getP()); 51     
52  t1.free(); 53  t2.free(); 54     
55     return 0; 56 }

何時須要進行深拷貝
-對象中有成員指代了系統中的資源
  成員指向了動態內存空間
  成員打開了外存中的文件
  成員使用了系統中的網絡端口
  ...

通常性原則
自定義拷貝構造函數,必然須要實現深拷貝

數組類的改進:

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 public: 10     IntArray(int len); 11     IntArray(const IntArray& obj); 12     int length(); 13     bool get(int index, int& value); 14     bool set(int index ,int value); 15     void free(); 16 }; 17 
18 #endif

IntArray.cpp

 1 #include "IntArray.h"
 2 
 3 IntArray::IntArray(int len)  4 {  5     m_pointer = new int[len];  6     
 7     for(int i=0; i<len; i++)  8  {  9         m_pointer[i] = 0; 10  } 11     
12     m_length = len; 13 } 14 
15 IntArray::IntArray(const IntArray& obj) 16 { 17     m_length = obj.m_length; 18     
19     m_pointer = new int[obj.m_length]; 20     
21     for(int i=0; i<obj.m_length; i++) 22  { 23         m_pointer[i] = obj.m_pointer[i]; 24  } 25 } 26 
27 int IntArray::length() 28 { 29     return m_length; 30 } 31 
32 bool IntArray::get(int index, int& value) 33 { 34     bool ret = (0 <= index) && (index < length()); 35     
36     if( ret ) 37  { 38         value = m_pointer[index]; 39  } 40     
41     return ret; 42 } 43 
44 bool IntArray::set(int index, int value) 45 { 46     bool ret = (0 <= index) && (index < length()); 47     
48     if( ret ) 49  { 50         m_pointer[index] = value; 51  } 52     
53     return ret; 54 } 55 
56 void IntArray::free() 57 { 58  delete[]m_pointer; 59 }

main.cpp

 1 #include <stdio.h>
 2 #include "IntArray.h"
 3 
 4 int main()  5 {  6     IntArray a(5);  7     
 8     for(int i=0; i<a.length(); i++)  9  { 10         a.set(i, i + 1); 11  } 12     
13     for(int i=0; i<a.length(); i++) 14  { 15         int value = 0; 16         
17         if( a.get(i, value) ) 18  { 19             printf("a[%d] = %d\n", i, value); 20  } 21  } 22     
23     IntArray b = a; 24     
25     for(int i=0; i<b.length(); i++) 26  { 27         int value = 0; 28         
29         if( b.get(i, value) ) 30  { 31             printf("b[%d] = %d\n", i, value); 32  } 33  } 34     
35  a.free(); 36  b.free(); 37     
38     return 0; 39 }
相關文章
相關標籤/搜索