zoukankan      html  css  js  c++  java
  • 第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 }
  • 相关阅读:
    Leetcode 92. Reverse Linked List II
    Leetcode 206. Reverse Linked List
    Leetcode 763. Partition Labels
    Leetcode 746. Min Cost Climbing Stairs
    Leetcode 759. Employee Free Time
    Leetcode 763. Partition Labels
    搭建数据仓库第09篇:物理建模
    Python进阶篇:Socket多线程
    Python进阶篇:文件系统的操作
    搭建数据仓库第08篇:逻辑建模–5–维度建模核心之一致性维度2
  • 原文地址:https://www.cnblogs.com/-glb/p/11795514.html
Copyright © 2011-2022 走看看