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 }
  • 相关阅读:
    图像处理笔记(十)
    图像处理笔记(九):频率域滤波
    图像处理笔记(八)
    图像处理笔记(七)
    图像处理笔记(六):灰度变换与空间滤波
    分布式
    关于python代码是编译执行还是解释执行
    python中的元类metaclass
    Python的函数参数传递
    递推思想
  • 原文地址:https://www.cnblogs.com/-glb/p/11795514.html
Copyright © 2011-2022 走看看