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\n",Ta.get_i());//Ta.i = 0
    printf("Ta.j = %d\n",Ta.get_j());//Ta.j = 0

    Test Tb;//在栈上定义类
    printf("Tb.i = %d\n",Tb.get_i());//Tb.i = 随机数
    printf("Tb.j = %d\n",Tb.get_j());//Tb.j = 随机数
    
    Test *Tc = new Test;//在堆上定义类
    printf("Tc->i = %d\n",Tc->get_i());//Tc.i = 随机数
    printf("Tc->j = %d\n",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()\n");
        i = 5; j = 10;
    }
};

Test Ta;

int main(int argc, char *argv[])
{
    printf("Ta.i = %d\n",Ta.get_i());
    printf("Ta.j = %d\n",Ta.get_j());

    Test Tb;
    printf("Tb.i = %d\n",Tb.get_i());
    printf("Tb.j = %d\n",Tb.get_j());
    
    Test *Tc = new Test;
    printf("Tc->i = %d\n",Tc->get_i());
    printf("Tc->j = %d\n",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()\n");
        i = 5; j = 10;
    }
    Test(int v)
    {
        printf("Test(int v);v = %d\n",v);
        i = 20; j = 30;
    }
};



int main(int argc, char *argv[])
{
    Test Ta;
    printf("Ta.i = %d\n",Ta.get_i());
    printf("Ta.j = %d\n",Ta.get_j());

    Test Tb(10);
    printf("Tb.i = %d\n",Tb.get_i());
    printf("Tb.j = %d\n",Tb.get_j());
    
    Test *Tc = new Test(30);
    printf("Tc->i = %d\n",Tc->get_i());
    printf("Tc->j = %d\n",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\n",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\t",Ta.get_i());
    printf("Ta.j = %d\n",Ta.get_j());    
    printf("Tb.i = %d\t",Tb.get_i());
    printf("Tb.j = %d\n",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\t",Ta.get_i());
    printf("Ta.j = %d\n",Ta.get_j());    
    printf("Tb.i = %d\t",Tb.get_i());
    printf("Tb.j = %d\n",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\t,Ta.j = %d\t,Ta.p = %p\n",Ta.get_i(),Ta.get_j(),Ta.get_p());
    printf("Tb.i = %d\t,Tb.j = %d\t,Tb.p = %p\n",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\n",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\n",i,temp);
    }
    if(TestArray.getArrayData(100,temp))
    printf("getArrayData(%d) = %d\n",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

 

  
 


 

原文链接: https://www.cnblogs.com/hjxzjp/p/11651330.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

    C++ 对象的构造

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/303924

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月16日 上午1:15
下一篇 2023年2月16日 上午1:17

相关推荐