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 }