c++中的类之构造函数

一、构造函数的缘由

  本文我们主要来讲解c++中类的构造函数,其中涉及了深拷贝和浅拷贝的问题,这也是在面试笔试中经常会碰到的问题。如果您是第一次听说构造函数,可能会觉得这个名字有点高大上,而它却和实际中的工程问题有关。在正式的讲解前,我们先来思考一个问题:(注意,本文采用的课件来源于狄泰软件学院,感谢狄泰的唐老师!)

  

 1 #include <stdio.h>
 2 
 3 class Test
 4 {
 5 private:
 6     int i;
 7     int j;
 8 public:
 9     int getI() { return i; }
10     int getJ() { return j; }
11 };
12 
13 Test gt;  // 全局变量放在bss段
14 
15 int main()
16 {
17     // 访问全局变量gt的的成员变量值
18     printf("gt.i = %d
", gt.getI());
19     printf("gt.j = %d
", gt.getJ());
20     
21     Test t1;
22     // 访问局部变量t1的的成员变量值
23     printf("t1.i = %d
", t1.getI());
24     printf("t1.j = %d
", t1.getJ());
25     
26     Test* pt = new Test;
27     // 访问堆空间pt的的成员变量值
28     printf("pt->i = %d
", pt->getI());
29     printf("pt->j = %d
", pt->getJ());
30     
31     delete pt;  // 注意释放堆空间
32     
33     return 0;
34 }
成员变量的初始值

   通过这个简单的例子,我们发现,同样是声明一个类的对象,因为对象所在的存储空间不同(bss段、堆空间、栈),导致其对象的成员变量的初始值不相同。对于我们类的使用者来说,我们当然不希望出现这种情况。于是,我们希望在定义一个类的对象的同时,初始化其成员变量的值,统一化,不管是在bss段,堆空间、栈上。

  

因此,我们可以提供下面的解决方案:

   

这样的方式虽然可以解决我们之前遇到的问题,但是在实际的使用过程中会觉得不好用。

  

为此,c++的设计者想出了一个办法,即构造函数:

  

注意,构造函数必须满足: 1. 与类的名字相同   2.无返回值

二、有参构造函数和重载构造函数

  

  

注意这里的初始化方式,即“ ()“” 和“ = ”。

  

在创建一个对象数组的时候,我们可以手工调用构造函数来初始化该对象数组。

 1 #include <stdio.h>
 2 
 3 class Test
 4 {
 5 private:
 6     int m_value;
 7 public:
 8     Test() 
 9     { 
10         printf("Test()
");
11         
12         m_value = 0;
13     }
14     Test(int v) 
15     { 
16         printf("Test(int v), v = %d
", v);
17         
18         m_value = v;
19     }
20     int getValue()
21     {
22         return m_value;
23     }
24 };
25 
26 int main()
27 {
28     Test ta[3] = {Test(), Test(1), Test(2)};  // 手工调用构造函数
29     
30     for(int i=0; i<3; i++)
31     {
32         printf("ta[%d].getValue() = %d
", i , ta[i].getValue());
33     }
34     
35     // int i(100); // 这样的方式是初始化i
36     Test t = Test(100); // 初始化方式
37     
38     printf("t.getValue() = %d
", t.getValue());
39     
40     return 0;
41 }
创建对象数组

三、无参构造函数和拷贝构造函数

 下面来介绍两个特殊的构造函数:

  

  

注意:没有构造函数是指连拷贝构造函数都没有。

3.2 深拷贝和浅拷贝

  

浅拷贝只是简单的成员变量复制。我们可以看下面的例子:

 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         
27         /* 这里是浅拷贝,p和t.p所指向的内存空间相同
28          */
29         // p = t.p ; 
30         
31         /* 注意这里是深拷贝,与浅拷贝不同的是,重新在堆空间申请内存空间,
32          * 并将该空间的值和t.p的相同,这样就可以使得对象的逻辑状态相同
33          */
34         p = new int;        
35         *p = *t.p;
36     }
37     Test(int v)
38     {
39         i = 1;
40         j = 2;
41         p = new int;        
42         *p = v;
43     }
44     void free()
45     {
46         delete p;
47     }
48 };
49 
50 int main()
51 {
52     Test t1(3);
53     Test t2(t1); 
54     
55     printf("t1.i = %d, t1.j = %d, *t1.p = %d
", t1.getI(), t1.getJ(), *t1.getP());
56     printf("t2.i = %d, t2.j = %d, *t2.p = %d
", t2.getI(), t2.getJ(), *t2.getP());
57     
58     t1.free();  // 释放两次,如果是浅拷贝会出问题,而深拷贝不会
59     t2.free();  
60     
61     return 0;
62 }
深拷贝和浅拷贝

  

既然我们已经了解了深拷贝和浅拷贝的区别,那么什么时候我们该使用深拷贝呢?

  (即申请堆空间、打开文件、网络端口等操作)

  


四、初始化列表

  在正式讲解初始化列表前,我们先来思考一个问题:

    

    

实际上,我们会发现这个类并没有给成员变量ci一个初始值,所以会出错。那么我们怎么样为const 成员变量初始化一个值呢?这里就引入了初始化列表的概念。如下:

    

可以看到初始化列表是在构造函数函数名之后加上一个冒号,之间用逗号隔开,m1、m2、m3是代表类的成员变量,括号里面的是对应的初始值。需要注意的是:

  

 1 #include <stdio.h>
 2 
 3 class Value
 4 {
 5 private:
 6     int mi;
 7 public:
 8     Value(int i)
 9     {
10         printf("i = %d
", i);
11         mi = i;
12     }
13     int getI()
14     {
15         return mi;
16     }
17 };
18 
19 class Test
20 {
21 private:
22     // 成员变量的初始化顺序和成员变量的声明顺序有关,而与初始化列表中的位置无关
23     Value m2;
24     Value m3;
25     Value m1;
26 public:
27     Test() : m1(1), m2(2), m3(3)
28     {
29         //由于初始化列表先于构造函数执行,所以这一句代码最后执行
30         printf("Test::Test()
"); 
31     }
32 };
33 
34 
35 int main()
36 {
37     Test t;
38     
39     return 0;
40 }
成员变量的初始化顺序

  我们再回到之前的那个问题:

  

一个小问题:

  

   (注意,本文采用的课件来源于狄泰软件学院,感谢狄泰的唐老师!)

原文地址:https://www.cnblogs.com/qiabaowei/p/8783772.html