第19课构造函数(下)

1. 两个特殊的构造函数
  -无参构造函数
    没有参数的构造函数
    解释:无参构造函数看似非常简单,但是它就特别在它是必须存在的。因为我们要使用一个类的话,就必须创建对象。创建对象必然牵涉到构造函数的调用。
    注意:当类中没有定义构造函数时,编译器默认提供一个无参构造函数,并且函数体为空。一定要特别注意,当类中没有构造函数时,编译器才会提供,否则不会提供。
  -拷贝构造函数
    参数为const class_name&的构造函数
     (判断一个构造函数是不是拷贝构造函数的依据:1、const 2、当前类对象的引用)只要看到这样的参数出现在一个构造函数里面,它必然的就是一个拷贝构造函数了。

    当类中没有定义拷贝构造函数时,编译器默认提供一个拷贝构造函数,简单的进行成员变量的值复制。

 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
", t1.getI(), t1.getJ());
33     printf("t2.i = %d, t2.j = %d
", t2.getI(), t2.getJ());
34     
35     return 0;
36 }

在上面这个例子中,一旦定义了

Test(const Test& t)
{
       i = t.i;
       j = t.j;
}
那么Test t1;就会出错。为什么?
因为找不到一个Test()的构造函数。
问题:不是编译器会默认提供一个无参构造函数吗?
一定要注意,编译器提供默认构造函数的前提,就是类中没有构造函数,一旦拥有,编译器就不再提供。

呈现一个经典面试题,下面的这个类,它里面有什么东西?

class T

{

};

它里面至少有一个无参构造函数,是编译器提供的。

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
", t1.getI(), t1.getJ(), t1.getP());
50     printf("t2.i = %d, t2.j = %d, *t2.p = %p
", 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
", t1.getI(), t1.getJ(), t1.getP());
50     printf("t2.i = %d, t2.j = %d, *t2.p = %p
", 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
", t1.getI(), t1.getJ(), t1.getP());
50     printf("t2.i = %d, t2.j = %d, *t2.p = %p
", 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
", 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
", i, value);
32         }
33     }
34     
35     a.free();
36     b.free();
37     
38     return 0;
39 }
原文地址:https://www.cnblogs.com/-glb/p/11795514.html