9.C -对象的构造函数(详解),9.c构造函数

时间:2019-11-09 07:39来源:计算机教程
9.C -对象的构造函数(详解),9.c构造函数 大家都定义struct或class时,不能给成员直接赋值,那么对象中成员变量的初始值是多少? 对于 局部对象变量 而言,其 成员是个随机值 ,因为该变量是

9.C -对象的构造函数(详解),9.c构造函数

大家都定义struct或class时,不能给成员直接赋值,那么对象中成员变量的初始值是多少?

对于局部对象变量而言,其成员是个随机值,因为该变量是被分配在栈上,对于其它局部变量也是这样.

对于全局对象变量而言,其成员都为0,因为该变量是被分配在静态存储区上,对于const修饰就是分配在只读静态存储区上.

对于使用malloc分配的对象变量而言,其成员是个随机值,分配的地址是存在堆上

对于使用new分配的对象变量而言,其成员都为0,由于分配的地址是存在堆上,很显然new分配出来的值是内部清0了

所以:

  • 栈上创建对象时,成员变量为随机值
  • 堆上创建对象时,成员变量初始为随机值
  • 静态存储区创建对象时,成员变量初始为0

 

构造函数

一般而言,对象创建时都会需要一个确定的初始状态

所以在C 中,引入了一个特殊函数-构造函数

  • 构造函数的名字必须与类名相同
  • 构造函数可以带参数,但是没有任何返回类型的声明,
  • 构造函数在创建对象时,会被自动调用

参考下面示例:

class Test
{
private:
    int i;
    int j;
public:
    int getI() { return i; }
    int getJ() { return j; }
    Test()                                    //构造函数
    {
        i = 1;
        j = 2;
    }
};

Test t;                         //创建全局对象t,并自动调用Test()来初始化 i=1  j=2

 

多个重载的构造函数

由于构造函数可以带参数,所以一个类可以存在多个重载的构造函数

例如:

class Test
{
public:
    Test(){    }
    Test(int i){  }
    Test(int i,float t){ }
};

和重载函数唯一不同的是,对于参数个数相同,参数类型不同,将会报错,比如:

class Test
{
public:
     Test(int i){  }
     Test(float t){  }      //报错
};

 

多个重载构造函数的调用

在之前小节,分析到构造函数是用来初始化对象的.如果有多个重载的构造函数,又如何来调用呢?

参考下面示例:

#include <stdio.h>
class Test
{
private:
         int  m_val;
public :
         Test()
         {
                   m_val=0;
                   printf("Test() n");
         }
         Test(int i)
         {
                   m_val=i;
                   printf("Test(int i)  i=%d n",i);
         }
         Test(float t,int i)
         {
                   m_val=i;
                   printf("Test(float t,int i)  t=%f i=%dn",t,i);
         }
};

int main()
{
         Test t1;                              //调用Test()初始化
         Test t2(1.1);                        //调用Test(int i) 初始化
         Test t4=1;                           //调用Test(int i) 初始化
         Test t3(1.5,2);                      //调用Test(float t,int i) 初始化

         Test t4=Test(3,4);                   //手工调用Test(float t,int i) 初始化

         t1=t4;                              //赋值操作,所以不会调用Test()初始化
         return 0;  
}

同样在C 中,也可以通过()来初始化变量,比如:

int i(100);                   //等价于int i=100;

注意,在C 中,初始化和赋值两个概念是不同的

比如:

int i=1;               //初始化
int j;                 //初始化

i=2;                     //赋值
j=1;                     //赋值

对象数组之手工调用构造函数

还是以上个Test类为例:

Test Tarray[3]={ Test(),Test(1), Test(2)};        //初始化对象数组里的m_val值分别为0,1,2; 

 

特殊的构造函数

-无参数构造函数

vnsc5858威尼斯城官网,当类中没有定义构造函数时,编译器会默认提供一个函数体为空的无参构造函数,

-拷贝构造函数 (参数为: const class_name&)

当类中没有定义拷贝构造函数时,编译器会默认提供一个拷贝构造函数,简单的进行成员变量的复制

1.接下来证明无参构造函数的存在,参考下面出错的示例

#include <stdio.h>

class Test
{
private:
         int  m_val;
public :
         int getm(void)
         {
            return m_val;
         }

         Test(const Test& t)          
         {
            m_val=t.m_val;
         }       
};

int main()
{
         Test t1;                                        
         return 0;
}

编译时, 报错:

test.cpp:21: error: no matching function for call to ‘Test::Test()’

提示说, 定义Test t1时,没有没匹配到Test()无参构造函数.

这是因为我们提供了构造函数,所以编译器就不再提供无参构造函数了,从而编译报错。

 

2.接下来来证明拷贝构造函数的存在,参考下面示例

#include <stdio.h>
class Test
{
private:
         int  m_val;
public :
         int getm(void)
         {
           return m_val;
         }

//      Test()
//      { 
//      }
//      Test(const Test& t)           //定义一个拷贝构造函数
//      {
//      printf("set m_val=%dn",t.m_val);
//         m_val= t.m_val;
//      }

};

int main()
{
         Test t1;                                //调用Test()初始化
         Test t2=t1;                         
         printf("t1.m_val=%d  t2.m_val=%d n",t1.getm(),t2.getm());              
         return 0;
}

运行打印:

t1.m_val=-1078151848  t2.m_val=-1078151848

可以发现打印的数据t1.m_valt2.m_val的值是一摸一样的,这是因为执行Test t2=t1;时,由于Test类里没有提供拷贝构造函数,所以编译器提供了一个拷贝构造函数。

我们取消上面示例的屏蔽,使用自定义的拷贝构造函数:

运行打印:

set m_val=-1076378568                            

t1.m_val=-1076378568  t2.m_val=-1076378568

从打印的数据上看到,执行Test t2=t1; 时,明显调用了我们自定义的Test::Test(const Test& t)拷贝函数.

所以当类中没有定义拷贝构造函数时,编译器会默认提供一个拷贝构造函数,进行简单的成员变量拷贝.

 

深入理解拷贝构造函数 

拷贝构造函数分为两种:

-浅拷贝(编译器提供的)

  拷贝后对象的物理状态相同

-深拷贝(指自己定义的)

  拷贝后对象的逻辑状态相同

接下来看浅拷贝和深拷贝的区别,参考下面示例:

#include <stdio.h>

class Test
{
private:
         int  m_val;
         int  *p;
public :
int getm()
         {
                   return m_val;
         }

int* getp()
         {
                   return p;
         }

        void free()
        {
          delete p;      
        }  

        Test(int i)
         {
            p= new int;
            m_val=0;
            *p=1;
         }

 //    Test(const Test& obj)
 //    {
 //             p=new int;
 //            
 //             m_val=t.m_val;
 //             *p=*obj.p;
 //    }
};

int main()
{
         Test t1(2);                                   //调用Test(int i)初始化          
         Test t2=t1;                                   //调用编译器提供的拷贝构造函数,进行浅拷贝

         printf("t1.m_val=%d t1.p=%p *t1.p=%dn",t1.getm(),t1.getp(),*t1.getp());
         printf("t2.m_val=%d t2.p=%p *t2.p=%dn",t2.getm(),t2.getp(),*t2.getp());

         t1.free();
         t2.free();

         return 0;
}

运行打印:

t1.m_val=0 t1.p=0x9fd1008 *t1.p=1        

t2.m_val=0 t2.p=0x9fd1008 *t2.p=1        

*** glibc detected *** ./a.out: double free or corruption (fasttop): 0x09fd1008 ***  

从打印结果看出,进行浅拷贝时,两个对象的成员指针都指向同一个地址0x9fd1008,可以发现当我们释放了t1对象的成员指针后,就不能继续使用t2对象的成员指针了.

接下来,我们取消上面示例的屏蔽,使用深拷贝,便能解决这类问题了.

 

那么什么时候需要进行深拷贝?

-当对象成员有指针时

-当对象成员需要打开文件时

-需要链接数据库时

 

总结:

既然,浅拷贝可以实现成员变量拷贝,所以,只要自定义拷贝构造函数,必然里面会实现深拷贝.

 

http://www.bkjia.com/cjjc/1312502.htmlwww.bkjia.comtruehttp://www.bkjia.com/cjjc/1312502.htmlTechArticle9.C -对象的构造函数(详解),9.c构造函数 大家都定义struct 或class 时, 不能给成员直接赋值, 那么对象中成员变量的初始值是多少? 对于 局部...

编辑:计算机教程 本文来源:9.C -对象的构造函数(详解),9.c构造函数

关键词: