• C++ 对象的构造


    在类里面成员函数的初始值是多少了?(取决于创建对象的位置,是在堆、栈、还是在静态存储区中创建。)

      例如:  

    #include <stdio.h>
    
    class Test
    {
        private:
        int i;
        int j;
        public :
        int get_i(void)    {return i;}
        int get_j(void)    {return j;}
    };
    
    Test Ta;//在静态存储区中定义 Test类
    
    int main(int argc, char *argv[])
    {
        printf("Ta.i = %d
    ",Ta.get_i());//Ta.i = 0
        printf("Ta.j = %d
    ",Ta.get_j());//Ta.j = 0
    
        Test Tb;//在栈上定义类
        printf("Tb.i = %d
    ",Tb.get_i());//Tb.i = 随机数
        printf("Tb.j = %d
    ",Tb.get_j());//Tb.j = 随机数
        
        Test *Tc = new Test;//在堆上定义类
        printf("Tc->i = %d
    ",Tc->get_i());//Tc.i = 随机数
        printf("Tc->j = %d
    ",Tc->get_j());//Tc.i = 随机数
        
        return 0;
    }

      运行结果:  

    Ta.i = 0
    Ta.j = 0
    Tb.i = 1808322352
    Tb.j = 32766
    Tc->i = 0
    Tc->j = 0

      可以看出,对象只是变量,所以在不同的地方定义变量,所的到的初始值也不同。

      在堆上定义:为随机数

      在栈上定义:为随机数

      在静态存储区上定义:因为静态存储区中变量默认为0 ,所以为0

    这样在不同地方定义初始值就会不同,这样是不允许的所以我们需要对变量进行初始化。这就引入了类的构造函数。

    构造函数:

      构造函数特点:

        1、构造函数没有任何返回类型的声明。

        2、构造函数在定义的时候被自动调用。

        例如:    

    #include <stdio.h>
    
    class Test
    {
        private:
        int i;
        int j;
        public :
        int get_i(void)    {return i;}
        int get_j(void)    {return j;}
        Test()
        {
            printf("Test()
    ");
            i = 5; j = 10;
        }
    };
    
    Test Ta;
    
    int main(int argc, char *argv[])
    {
        printf("Ta.i = %d
    ",Ta.get_i());
        printf("Ta.j = %d
    ",Ta.get_j());
    
        Test Tb;
        printf("Tb.i = %d
    ",Tb.get_i());
        printf("Tb.j = %d
    ",Tb.get_j());
        
        Test *Tc = new Test;
        printf("Tc->i = %d
    ",Tc->get_i());
        printf("Tc->j = %d
    ",Tc->get_j());
        
        return 0;
    }

      在类中加入构造函数。

      运行结果:  

    Test()
    Ta.i = 5
    Ta.j = 10
    Test()
    Tb.i = 5
    Tb.j = 10
    Test()
    Tc->i = 5
    Tc->j = 10

      可以看出每次定义都调用了一次构造函数。

    一个类中可以有多个构造函数构成重载,重载的概念在类中同样适用。

      例如:

      

    #include <stdio.h>
    
    class Test
    {
        private:
        int i;
        int j;
        public :
        int get_i(void)    {return i;}
        int get_j(void)    {return j;}
        Test()
        {
            printf("Test()
    ");
            i = 5; j = 10;
        }
        Test(int v)
        {
            printf("Test(int v);v = %d
    ",v);
            i = 20; j = 30;
        }
    };
    
    
    
    int main(int argc, char *argv[])
    {
        Test Ta;
        printf("Ta.i = %d
    ",Ta.get_i());
        printf("Ta.j = %d
    ",Ta.get_j());
    
        Test Tb(10);
        printf("Tb.i = %d
    ",Tb.get_i());
        printf("Tb.j = %d
    ",Tb.get_j());
        
        Test *Tc = new Test(30);
        printf("Tc->i = %d
    ",Tc->get_i());
        printf("Tc->j = %d
    ",Tc->get_j());
        
        
        return 0;
    }

      运行结果:  

    Test()
    Ta.i = 5
    Ta.j = 10
    Test(int v);v = 10
    Tb.i = 20
    Tb.j = 30
    Test(int v);v = 30
    Tc->i = 20
    Tc->j = 30

    从结果中可以看出:

     Test Ta;调用的是 Test() 这个构造函数。
    Test Tb(10);和 Test *Tc = new Test(30);调用的是   Test(int v) 这个构造函数。

    注意:对象的定义与对象的声明是不同的。例如变量的定义与变量的声明也是不同的。
      对象定义:声明对象的空间并调用构造函数。
      对象的声明:告诉编译器存在这样的一个变量。
    构造函数的手动调用:
      一般来说构造函数在定义对象的时候被自动调用,但是在一些特殊情况下需要手动调用。
      例如构造对象数组。
    实验:创建一个数组类解决数组的安全性问题。
      1、创建Intarray.h
        
    #ifndef __INTARRAY_H
    #define __INTARRAY_H
    class intArray
    {
        private:
        int arrayLenght;
        int *Parray;
        public:
        intArray (int lenght);//构造函数
        bool changeArray(int index,int val);//修改数组中的元素
        int getLenght(void);//获取数组长度
        bool getArrayData(int index,int& val);//获取数组中的元素
        void free();
    };
    
    
    #endif 
      2、创建Intarray.cpp
      
    #include "intArray.h"
    
    intArray::intArray (int lenght)//构造函数
    {
        Parray = new int[lenght];//创建数组空间
        for(int i=0; i<lenght; i++)//初始化
        Parray[i] = 0;
        arrayLenght = lenght;
    }
    bool intArray::changeArray(int index,int val)//修改数组中的元素
    {
        bool ret = (index>=0)&&(index < arrayLenght);//判断是否越界
        if(ret)
        {
            Parray[index] = val;
        }
        return ret;
    }
    int intArray::getLenght(void)//获取数组长度
    {
        return arrayLenght;
    }
    bool intArray::getArrayData(int index, int& val)//获取数组中的元素
    {
        bool ret = (index>=0)&&(index < arrayLenght);//判断是否越界
        if(ret)
        {
            val =  Parray[index] ;
        }
        return ret;
    }
    
    void intArray::free()//
    {
        delete[] Parray;
    }

       3、创建main.cpp

    #include <stdio.h>
    #include "intArray.h"
    
    
    int main(int argc, char *argv[])
    {
        int temp ;
        intArray TestArray(6);
        for(int i=0; i<TestArray.getLenght();i++)
            TestArray.changeArray(i,i);
        for(int i=0; i<TestArray.getLenght();i++)
        {
            if(TestArray.getArrayData(i,temp))
    printf(
    "getArrayData(%d) = %d ",i,temp); } TestArray.free(); return 0; }

    运行结果:

    getArrayData(0) = 0
    getArrayData(1) = 1
    getArrayData(2) = 2
    getArrayData(3) = 3
    getArrayData(4) = 4
    getArrayData(5) = 5
    类中的特殊构造函数:
      1、无参构造函数。(当类中没有定义任何构造函数时,编译器会默认的提供一个无参构造函数,函数体为空
        class_name(){}
      2、拷贝构造函数。参数为const class_name& 的构造函数 (当类中没有定义任何拷贝构造函数时,编译器为默认提供一个拷贝构造函数,其功能为进行成员变量的赋值。)
        例如:定义一个对象的时候使用另外一个对象对其进行初始化。
        class_name class1;
        class_name class2=class1;或者(
    class_name class2(class1);
    )
        通过以上使用就需要用到拷贝构造函数,编译器默认的拷贝构造函数保证的是两个对象的物理状态相同(浅拷贝)。也就是说这是一种 浅拷贝。那么有浅拷贝就必然有深拷贝(其作用是保证两个对象在逻辑状态上相同)。
      例如代码:  
    #include <stdio.h>
    
    class Test
    {
        private:
        int i;
        int j;
        public :
        int get_i(void)    {return i;}
        int get_j(void)    {return j;}
        
    };
    
    
    
    int main(int argc, char *argv[])
    {
        Test Ta;
        Test Tb(Ta);
        printf("Ta.i = %d	",Ta.get_i());
        printf("Ta.j = %d
    ",Ta.get_j());    
        printf("Tb.i = %d	",Tb.get_i());
        printf("Tb.j = %d
    ",Tb.get_j());    
        return 0;
    }
    
    

    运行结果:  

    Ta.i = -1553435232    Ta.j = 22062
    Tb.i = -1553435232    Tb.j = 22062

      从运行结果中可以看出,对象Tb 与对象Ta中的变量i,j值完全相同。

    修改代码:添加拷贝构造函数。

      

    #include <stdio.h>
    
    class Test
    {
        private:
        int i;
        int j;
        public :
        int get_i(void)    {return i;}
        int get_j(void)    {return j;}
        Test(){};
        Test(const Test& t)
        {
          i = t.i;
          j = t.j;
        }
    };
    
    
    
    int main(int argc, char *argv[])
    {
        Test Ta;
        Test Tb(Ta);
        printf("Ta.i = %d	",Ta.get_i());
        printf("Ta.j = %d
    ",Ta.get_j());    
        printf("Tb.i = %d	",Tb.get_i());
        printf("Tb.j = %d
    ",Tb.get_j());    
        return 0;
    }

    运行结果:从结果中可以看出结果同上面没有加入拷贝构造函数时一致。也就是说编译器给我们默认构造了一个拷贝构造函数。内容与下面代码一致  

    Test(const Test& t)
        {
          i = t.i;
          j = t.j;
        }
    Ta.i = 688973216    Ta.j = 22083
    Tb.i = 688973216    Tb.j = 22083

    其中类中成员没有指代系统中的资源。所以看起来没有什么问题。

      修改代码:增加int *p = new int;并打印出p的地址

      

    #include <stdio.h>
    
    class Test
    {
        private:
        int i;
        int j;
        int *p ;
        public :
        int get_i(void)    {return i;}
        int get_j(void)    {return j;}
        int* get_p(void){return p;}
        void free(void){delete p;}
        Test(int v)
        {
            i=1;
            j =2;
            p = new int;
            *p = v;
        };
        Test(const Test& t)
        {
          i = t.i;
          j = t.j;
          p = new int;
         *p = *t.p;
        }
    };
    
    
    
    int main(int argc, char *argv[])
    {
        Test Ta(2);
        Test Tb(Ta);
        printf("Ta.i = %d	,Ta.j = %d	,Ta.p = %p
    ",Ta.get_i(),Ta.get_j(),Ta.get_p());
        printf("Tb.i = %d	,Tb.j = %d	,Tb.p = %p
    ",Tb.get_i(),Tb.get_j(),Tb.get_p());
        Ta.free();
        Tb.free();
        return 0;
    }

    运行结果:  

    Ta.i = 1    ,Ta.j = 2    ,Ta.p = 0x55d66fe34e70
    Tb.i = 1    ,Tb.j = 2    ,Tb.p = 0x55d66fe34e90

    如果在拷贝构造函数中去掉  p = new int;   *p = *t.p;

      运行结果:  

    Ta.i = 1    ,Ta.j = 2    ,Ta.p = 0x55dbf6d60e70
    Tb.i = 1    ,Tb.j = 2    ,Tb.p = (nil)

    申请的 p 指针为空。这显然是不对的。

      打印p所指向空间的值运行结果:  

    Ta.i = 1    ,Ta.j = 2    ,Ta.p = 0x563d1529ee70    ,*Ta.p=2
    Tb.i = 1    ,Tb.j = 2    ,Tb.p = 0x563d1377d9fd    ,*Ta.p=29590344
    munmap_chunk(): invalid pointer
    Aborted (core dumped)

      指针在释放的过程中出现了错误。

    在拷贝构造函数中 增加 p = new int;   *p = *t.p;

      运行结果:  

    Ta.i = 1    ,Ta.j = 2    ,Ta.p = 0x55b993806e70    ,*Ta.p=2
    Tb.i = 1    ,Tb.j = 2    ,Tb.p = 0x55b993806e90    ,*Ta.p=2

    关于深拷贝的说明:  ——自定义拷贝函数,必然需要使用到深拷贝 

      到底什么时候需要用到深拷贝?    ——对象中有成员指代了系统资源。

      1、成员指向了动态内存空间。

      2、成员打开了外部文件。

      3、成员使用了系统中的网络端口

    我们上面的实验使用到了动态内存空间。所以也会出现问题。需要给它加上自定义拷贝函数。
    修改代码如下:
    intArray.cpp 
    intArray::intArray (const intArray& obj)
    {
        Parray = new int[obj.arrayLenght];
        arrayLenght =     obj.arrayLenght;
        for(int i=0;i<obj.arrayLenght;i++)
        Parray[i] = obj.Parray[i];
    }
    
    

    main.cpp

      

    #include <stdio.h>
    #include "intArray.h"
    
    
    int main(int argc, char *argv[])
    {
        int temp ;
        intArray TestArray(6);
        for(int i=0; i<TestArray.getLenght();i++)
            TestArray.changeArray(i,i);
        for(int i=0; i<TestArray.getLenght();i++)
        {
            if(TestArray.getArrayData(i,temp))
            printf("getArrayData(%d) = %d
    ",i,temp);
        }
        intArray TestArray1(TestArray);
        for(int i=0; i<TestArray1.getLenght();i++)
            TestArray1.changeArray(i,i);
        for(int i=0; i<TestArray1.getLenght();i++)
        {
            if(TestArray1.getArrayData(i,temp))
            printf("getArrayData1(%d) = %d
    ",i,temp);
        }
        if(TestArray.getArrayData(100,temp))
        printf("getArrayData(%d) = %d
    ",100,temp);
        TestArray.free();
        TestArray1.free();
        return 0;
    }

      运行结果:

      

    getArrayData(0) = 0
    getArrayData(1) = 1
    getArrayData(2) = 2
    getArrayData(3) = 3
    getArrayData(4) = 4
    getArrayData(5) = 5
    getArrayData1(0) = 0
    getArrayData1(1) = 1
    getArrayData1(2) = 2
    getArrayData1(3) = 3
    getArrayData1(4) = 4
    getArrayData1(5) = 5
      
     


  • 相关阅读:
    友盟上报 IOS
    UTF8编码
    Hill加密算法
    Base64编码
    Logistic Regression 算法向量化实现及心得
    152. Maximum Product Subarray(中等, 神奇的 swap)
    216. Combination Sum III(medium, backtrack, 本类问题做的最快的一次)
    77. Combinations(medium, backtrack, 重要, 弄了1小时)
    47. Permutations II(medium, backtrack, 重要, 条件较难思考)
    3.5 find() 判断是否存在某元素
  • 原文地址:https://www.cnblogs.com/hjxzjp/p/11651330.html
Copyright © 2020-2023  润新知