zoukankan      html  css  js  c++  java
  • 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 }
    成员变量的初始化顺序

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

      

    一个小问题:

      

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

  • 相关阅读:
    C语言关键字register、extern、static、一些总结,及项目中使用的心得
    c语言,文件操作总结
    《Redis内存数据库》Redis内存数据库技术总结
    《Redis内存数据库》Redis环境搭建(Linux)
    《Linux 操作系统》Linux的常用命令操作大全
    《Java练习题》Java编程题合集(全)
    《Java练习题》Java习题集一
    《Java基础知识》Java技术总结
    《Java基础知识》Java数据类型以及变量的定义
    《Java 底层原理》Jvm 类的加载原理
  • 原文地址:https://www.cnblogs.com/qiabaowei/p/8783772.html
Copyright © 2011-2022 走看看