码迷,mamicode.com
首页 > 其他好文 > 详细

11 对象的构造

时间:2020-09-24 20:55:41      阅读:40      评论:0      收藏:0      [点我收藏+]

标签:containe   方式   shell   网络   必须   深浅拷贝   amp   通过   jpg   

1 对象的初始化

  • 问题:对象中成员变量初始值是多少?

    • Demo

      #include <stdio.h>
      
      class Test
      {
      private:
          int i;
          int j;
      public:
          int getI() { return i; }
          int getJ() { return j; }
      };
      
      Test gt;  //全局对象:静态存储区
      
      int main()
      {
          printf("gt.i = %d\n", gt.getI());  //0
          printf("gt.j = %d\n", gt.getJ());  //0
          
          Test t1;  //栈
          
          printf("t1.i = %d\n", t1.getI());  //134513995
          printf("t1.j = %d\n", t1.getJ());  //5984244
          
          Test* pt = new Test;  //堆
          
          printf("pt->i = %d\n", pt->getI());  //
          printf("pt->j = %d\n", pt->getJ());  //
          
          delete pt;
          
          return 0;
      }
      
  • 从程序设计的角度,对象只是变量,因此

    • 上创建对象时,成员变量初始为随机值
    • 上创建对象时,成员变量初始为随机值
    • 静态存储区上创建对象时,成员变量初始为 0 值
  • 问题:程序中如何对一个对象进行初始化,使得不管在什么地方创建类对象,其成员变量的初始值都为固定值?

  • 解决方案1

    • 在类中提供一个 publicinitialize 函数

    • 对象创建后立即调用 initialize 函数进行初始化

    • 示例:初始化函数

      • Demo

        #include <stdio.h>
        
        class Test
        {
        private:
            int i;
            int j;
        public:
            int getI() { return i; }
            int getJ() { return j; }
            //初始化函数
            void initialize()
            {
                i = 1;
                j = 2;
            }
        };
        
        Test gt;
        
        int main()
        {
            //显式调用初始化函数
            gt.initialize();
            
            printf("gt.i = %d\n", gt.getI());  //1
            printf("gt.j = %d\n", gt.getJ());  //2
            
            Test t1;
            
            //没有显式调用初始化函数,运行结果未知
            //t1.initialize();
            
            printf("t1.i = %d\n", t1.getI());  //7469952
            printf("t1.j = %d\n", t1.getJ());  //134514267
            
            //没有立即调用,运行结果未知
            t1.initialize();
            
            Test* pt = new Test;
            
            //显式调用初始化函数
            pt->initialize();
            
            printf("pt->i = %d\n", pt->getI());  //1
            printf("pt->j = %d\n", pt->getJ());  //2
            
            delete pt;
            
            return 0;
        }
        
    • 存在的问题

      • initialize 只是一个普通函数,必须显式调用
      • 如果未调用 initialize 函数,运行结果是不确定的
      • 如果没有在生成类对象后立即调用 initialize 函数 ,运行结果是不确定的
  • 解决方案2:构造函数

2 构造函数

2.1 构造函数简介

  • C++ 中可以定义与类名相同的特殊的成员函数,这种特殊的成员函数叫做构造函数

    • 构造函数没有任何返回类型的声明
    • 构造函数在对象定义时自动被调用
  • 示例:构造函数

    • Demo

      #include <stdio.h>
      
      class Test
      {
      private:
          int i;
          int j;
      public:
          int getI() { return i; }
          int getJ() { return j; }
          //构造函数
          Test()
          {
              printf("Test() Begin\n");
              
              i = 1;
              j = 2;
              
              printf("Test() End\n");
          }
      };
      
      Test gt;
      
      int main()
      {
          printf("gt.i = %d\n", gt.getI());
          printf("gt.j = %d\n", gt.getJ());
          
          Test t1;
          
          printf("t1.i = %d\n", t1.getI());
          printf("t1.j = %d\n", t1.getJ());
          
          Test* pt = new Test;
          
          printf("pt->i = %d\n", pt->getI());
          printf("pt->j = %d\n", pt->getJ());
          
          delete pt;
          
          return 0;
      }
      
    • 编译运行

      Test() Begin
      Test() End
      gt.i = 1
      gt.j = 2
      Test() Begin
      Test() End
      t1.i = 1
      t1.j = 2
      Test() Begin
      Test() End
      pt->i = 1
      pt->j = 2
      

2.2 带参数的构造函数

  • 带有参数的构造函数

    • 构造函数可以根据需要定义参数

    • 一个类中可以存在多个重载的构造函数

    • 构造函数的重载遵循 C++ 重载的规则

      class Test
      {
      public:
          Test(int c)
          {
              //use v to initialize member
          }
      };
      
  • 注意:对象定义对象声明不同

    • 对象定义:申请对象的空间并调用构造函数

    • 对象声明:告诉编译器存在这样一个对象

      Test t;  //定义对象并调用构造函数
      
      int main()
      {
          extern Test t;  //告诉编译器存在名为t的Test对象,通过链接器在各个目标文件中寻找t的定义
          
          return 0;
      }
      
  • 构造函数的自动调用

    class Test
    {
    public:
        Test(){}
        Test(int v){}
    };
    
    int main()
    {
        Test t;  //调用Test()
        Test t1(1);  //调用Test(int v)
        Test t2 = 1;  //调用Test(int v)
    }
    
  • 示例:带参数的构造函数

    • Demo

      #include <stdio.h>
      
      class Test
      {
      public:
          Test() 
          { 
              printf("Test()\n");
          }
          Test(int v) 
          { 
              printf("Test(int v), v = %d\n", v);
          }
      };
      
      int main()
      {
          Test t;      // 调用 Test()
          Test t1(1);  // 调用 Test(int v)
          Test t2 = 2; // 调用 Test(int v)
          
          int i(100);  //初始化(如果是类类型,会调用构造函数)
          
          int j;
          j = 2;  //赋值操作
          
          printf("i = %d\n", i);
          
          return 0;
      }
      
    • 编译运行

      Test()
      Test(int v), v = 1
      Test(int v), v = 2
      i = 100
      
  • 构造函数的调用

    • 一般情况下,构造函数在对象定义时被自动调用
    • 一些特殊情况下,需要手工调用构造函数,例如创建一个对象数组
  • 示例:构造函数的手动调用 => 创建一个对象数组

    • Demo

      #include <stdio.h>
      
      class Test
      {
      private:
          int m_value;
      public:
          Test() {
              printf("Test()\n");
              m_value = 0;
          }
          Test(int v) {
              printf("Test(int v),v = %d\n", v);
              m_value = v;
          }
          int getValue() {
              return m_value;
          }
      };
      
      int main()
      {
          Test ta[3];
      
          for (int i = 0; i < 3; ++i) {
              printf("ta[%d].getValue() = %d\n", i, ta[i].getValue());
          }
      
          return 0;
      }
      
    • 编译运行:可以看到对象数组 ta 中的每一个类对象的成员变量 m_value 值都相同:0

      Test()
      Test()
      Test()
      ta[0].getValue() = 0
      ta[1].getValue() = 0
      ta[2].getValue() = 0
      
    • 修改:创建对象值不同的对象数组

      #include <stdio.h>
      
      class Test
      {
      private:
          int m_value;
      public:
          Test() { 
              printf("Test()\n");
              m_value = 0;
          }
          Test(int v) { 
              printf("Test(int v), v = %d\n", v);
              m_value = v;
          }
          int getValue() {
              return m_value;
          }
      };
      
      int main()
      {
          Test ta[3] = {Test(), Test(1), Test(2)};  //手动调用构造函数
          
          for(int i=0; i<3; i++) {
              printf("ta[%d].getValue() = %d\n", i , ta[i].getValue());
          }
          
          Test t = Test(100);  //手动调用构造函数 => 完成定义对象
          
          printf("t.getValue() = %d\n", t.getValue());
          
          return 0;
      }
      
    • 编译运行

      Test()
      Test(int v), v = 1
      Test(int v), v = 2
      ta[0].getValue() = 0
      ta[1].getValue() = 1
      ta[2].getValue() = 2
      Test(int v), v = 100
      t.getValue() = 100
      
  • 需求:开发一个数组类解决原生数组的安全性问题

    • 提供函数获取数组长度

    • 提供函数获取数组元素

    • 提供函数设置数组元素

    • Demo:IntArray

      //IntArray.h
      #ifndef _INTARRAY_H_
      #define _INTARRAY_H_
      
      class IntArray
      {
      private:
          int m_length;  //长度
          int* m_pointer;  //数据
      public:
          IntArray(int len);  //构造函数
          int length();  //获取长度
          bool get(int index, int& value);  //获取数组指定位置的元素
          bool set(int index ,int value);  //设置数组指定位置的元素
          void free();  //释放所申请的堆空间
      };
      
      #endif
      
      
      //IntArray.cpp
      #include "IntArray.h"
      
      IntArray::IntArray(int len)
      {
          m_pointer = new int[len];
          
          for(int i=0; i<len; i++)
          {
              m_pointer[i] = 0;
          }
          
          m_length = len;
      }
      
      int IntArray::length()
      {
          return m_length;
      }
      
      bool IntArray::get(int index, int& value)
      {
          bool ret = (0 <= index) && (index < length());
          
          if( ret )
          {
              value = m_pointer[index];
          }
          
          return ret;
      }
      
      bool IntArray::set(int index, int value)
      {
          bool ret = (0 <= index) && (index < length());
          
          if( ret )
          {
              m_pointer[index] = value;
          }
          
          return ret;
      }
      
      void IntArray::free()
      {
          delete[] m_pointer;
      }
      
      
      //main.cpp
      #include <stdio.h>
      #include "IntArray.h"
      
      int main()
      {
          IntArray a(5);    
          
          for(int i=0; i<a.length(); i++)
          {
              a.set(i, i + 1);
          }
          
          for(int i=0; i<a.length(); i++)
          {
              int value = 0;
              
              if( a.get(i, value) )
              {
                  printf("a[%d] = %d\n", i, value);
              }
          }
          
          a.free();
          
          return 0;
      }
      
    • 编译运行

      a[0] = 1
      a[1] = 2
      a[2] = 3
      a[3] = 4
      a[4] = 5
      

2.3 无参构造函数和拷贝构造函数

  • 两个特殊的构造函数

    • 无参构造函数
      • 没有参数的构造函数
      • 当类中没有定义任何构造函数时,编译器会默认提供一个无参构造函数,并且其函数体为空
    • 拷贝构造函数
      • 参数为 const class_name& 的构造函数
      • 当类中没有定义拷贝构造函数时,编译器默认提供一个拷贝构造函数,简单的进行成员变量的值复制(浅拷贝)
  • 示例:特殊的构造函数

    • Demo

      #include <stdio.h>
      
      class Test
      {
      private:
          int i;
          int j;
      public:
          int getI()
          {
              return i;
          }
          int getJ()
          {
              return j;
          }
          /*Test(const Test& t)
          {
              i = t.i;
              j = t.j;
          }
          Test()
          {
          }*/
      };
      
      int main()
      {
          Test t1;
          Test t2 = t1;
          
          printf("t1.i = %d, t1.j = %d\n", t1.getI(), t1.getJ());
          printf("t2.i = %d, t2.j = %d\n", t2.getI(), t2.getJ());
          
          return 0;
      }
      
  • 拷贝构造函数的意义

    • 兼容 C 语言的初始化方式,形如:int i = 1; int j = i;
    • 初始化行为能够符合预期的逻辑:两个对象的状态一样
  • 深浅拷贝

    • 浅拷贝:拷贝后对象的物理状态相同,编译器提供的拷贝构造函数只进行浅拷贝
    • 深拷贝:拷贝后对象的逻辑状态相同
  • 示例:对象的初始化

    • Demo

      #include <stdio.h>
      
      class Test
      {
      private:
          int i;
          int j;
          int* p;
      public:
          int getI()
          {
              return i;
          }
          int getJ()
          {
              return j;
          }
          int* getP()
          {
              return p;
          }
          /*
          Test(const Test& t)
          {
              i = t.i;
              j = t.j;
              p = new int;
              
              *p = *t.p;
          }
          */
          Test(int v)
          {
              i = 1;
              j = 2;
              p = new int;
              
              *p = v;
          }
          void free()
          {
              delete p;
          }
      };
      
      int main()
      {
          Test t1(3);
          Test t2(t1);
          
          printf("t1.i = %d, t1.j = %d, *t1.p = %d\n", t1.getI(), t1.getJ(), *t1.getP());
          printf("t2.i = %d, t2.j = %d, *t2.p = %d\n", t2.getI(), t2.getJ(), *t2.getP());
          
          t1.free();
          t2.free();  //内存泄漏:0x8e6a008被释放了两次
          
          return 0;
      }
      
    • 编译运行

      t1.i = 1, t1.j = 2, t1.p = 0x8e6a008
      t2.i = 1, t2.j = 2, t2.p = 0x8e6a008
      
  • 问题:什么时候需要进行深拷贝?

    • 对象中有成员指代了系统中的资源

      • 成员指向了动态内存空间
      • 成员打开了外存中的文件
      • 成员使用了系统中的网络端口
      • 。。。。。。
    • 分析:t1t2m_pointer 指向同一块内存,之后会被释放两次

      技术图片

  • 一般性原则:自定义拷贝构造函数,必然需要实现深拷贝

  • IntArray 类的改进

    • Demo

      //IntArray.h
      #ifndef _INTARRAY_H_
      #define _INTARRAY_H_
      
      class IntArray
      {
      private:
          int m_length;
          int* m_pointer;
      public:
          IntArray(int len);
          IntArray(const IntArray& obj);  //拷贝构造函数
          int length();
          bool get(int index, int& value);
          bool set(int index ,int value);
          void free();
      };
      
      #endif
      
      
      //IntArray.cpp
      #include "IntArray.h"
      
      IntArray::IntArray(int len)
      {
          m_pointer = new int[len];
          
          for(int i=0; i<len; i++)
          {
              m_pointer[i] = 0;
          }
          
          m_length = len;
      }
      
      IntArray::IntArray(const IntArray& obj)
      {
          m_length = obj.m_length;
          
          m_pointer = new int[obj.m_length];
          
          for(int i=0; i<obj.m_length; i++)
          {
              m_pointer[i] = obj.m_pointer[i];
          }
      }
      
      int IntArray::length()
      {
          return m_length;
      }
      
      bool IntArray::get(int index, int& value)
      {
          bool ret = (0 <= index) && (index < length());
          
          if( ret )
          {
              value = m_pointer[index];
          }
          
          return ret;
      }
      
      bool IntArray::set(int index, int value)
      {
          bool ret = (0 <= index) && (index < length());
          
          if( ret )
          {
              m_pointer[index] = value;
          }
          
          return ret;
      }
      
      void IntArray::free()
      {
          delete[] m_pointer;
      }
      
      
      //main.cpp
      #include <stdio.h>
      #include "IntArray.h"
      
      int main()
      {
          IntArray a(5);    
          
          for(int i=0; i<a.length(); i++)
          {
              a.set(i, i + 1);
          }
          
          for(int i=0; i<a.length(); i++)
          {
              int value = 0;
              
              if( a.get(i, value) )
              {
                  printf("a[%d] = %d\n", i, value);
              }
          }
          
          IntArray b = a;
          
          for(int i=0; i<b.length(); i++)
          {
              int value = 0;
              
              if( b.get(i, value) )
              {
                  printf("b[%d] = %d\n", i, value);
              }
          }
          
          a.free();
          b.free();
          
          return 0;
      }
      
    • 编译运行

      1
      2
      3
      4
      5
      1
      2
      3
      4
      5
      

11 对象的构造

标签:containe   方式   shell   网络   必须   深浅拷贝   amp   通过   jpg   

原文地址:https://www.cnblogs.com/bky-hbq/p/13715195.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!