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

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

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

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

对于使用new分配的对象变量而言,其成员也为随机,因为用户分配的地址是存在堆上

所以:

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

构造函数

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

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

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

参考下面示例:

class Test
{
private:
int i;
int j;
public:
int getI() { return i; }
int getJ() { return j; }
Test() //构造函数
{
i = ;
j = ;
}
}; Test t; //创建全局对象t,并自动调用Test()来初始化 i=1 j=2

多个重载的构造函数

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

例如:

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

多个重载构造函数的调用

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

参考下面示例:

#include <stdio.h>
class Test
{
private:
int m_val;
public :
Test()
{
m_val=;
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=%d\n",t,i);
}
}; int main()
{
Test t1; //调用Test()初始化
Test t2(); //调用Test(int i) 初始化
Test t4=; //调用Test(int i) 初始化
Test t3(1.5f,); //调用Test(float t,int i) 初始化 Test t4=Test(,); //调用Test(3,4)返回一个临时对象,然后通过拷贝构造函数来初始化t4 t1=t4; //赋值操作,所以不会调用Test()初始化
return ;
}

为什么使用Test t4=1 能调用Test (4)?

当构造函数的参数只有一个时,并且参数是其它类型,该构造函数便称为转换构造函数

所以编译Test t4=1时,编译器会通过1来查找哪个构造函数的参数满足它,若没找到则编译报错.

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

int i();                   //转换为 int i=100;

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

还是以上个Test类为例:

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

从上面可以看出,一个构造函数其实是有返回值的,返回的是一个临时对象,然后赋值给Tarray[]数组里。

这个临时对象仅仅在调用时有效,执行下个代码时,就会被注销。

临时对象在后面第11章会讲到:11.C++-临时对象分析

特殊的构造函数

-无参数构造函数

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

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

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

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

#include <stdio.h>

class Test
{
private:
int m_val;
public :
int getm(void)
{
return m_val;
} Test(int val)
{
m_val=val;
}
}; int main()
{
Test t1;
return ;
}

编译时, 报错:

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

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

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

也可以将上面的Test(int val)改为:

   Test(int val=0)
{
m_val=val;
}

这样,就相当于提供了两个函数: Test(int val), Test().

当我们调用Test(1)时,则val=1.

当我们调用Test()时,则val=0.

这是C++新加的特性,在C里是没有该功能

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=%d\n",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 ;
}

运行打印:

t1.m_val=-  t2.m_val=-

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

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

运行打印:

set m_val=-                            

t1.m_val=-  t2.m_val=-

从打印的数据上看到,执行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)
{
       delete p;  
p= new int(i);
m_val=;
} // Test(const Test& obj)
// {
// p=new int;
//
// m_val=t.m_val;
// *p=*obj.p;
// }
}; int main()
{
Test t1(); //调用Test(int i)初始化
Test t2=t1; //调用编译器提供的拷贝构造函数,进行浅拷贝 printf("t1.m_val=%d t1.p=%p *t1.p=%d\n",t1.getm(),t1.getp(),*t1.getp());
printf("t2.m_val=%d t2.p=%p *t2.p=%d\n",t2.getm(),t2.getp(),*t2.getp());
t1.free();
t2.free(); return ;
}

运行打印:

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

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

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

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

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

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

-当对象成员有指针时

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

-需要链接数据库时

总结:

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

下章继续学习:10.C++-构造函数初始化列表、对象构造顺序、析构函数

9.C++-对象的构造函数(详解)的更多相关文章

  1. [转]c++类的构造函数详解

    c++构造函数的知识在各种c++教材上已有介绍,不过初学者往往不太注意观察和总结其中各种构造函数的特点和用法,故在此我根据自己的c++编程经验总结了一下c++中各种构造函数的特点,并附上例子,希望对初 ...

  2. C++构造函数详解及显式调用构造函数

    来源:http://www.cnblogs.com/xkfz007/archive/2012/05/11/2496447.html       c++类的构造函数详解                  ...

  3. C++中构造函数详解及显式调用构造函数

    C++构造函数详解及显式调用构造函数                                         c++类的构造函数详解                        一. 构造函 ...

  4. c++构造函数详解

    c++构造函数的知识在各种c++教材上已有介绍,不过初学者往往不太注意观察和总结其中各种构造函数的特点和用法,故在此我根据自己的c++编程经验总结了一下c++中各种构造函数的特点,并附上例子,希望对初 ...

  5. c++类的构造函数详解

    c++类的构造函数详解 一. 构造函数是干什么的 class Counter{ public:         // 类Counter的构造函数         // 特点:以类名作为函数名,无返回类 ...

  6. 【转载】图说C++对象模型:对象内存布局详解

    原文: 图说C++对象模型:对象内存布局详解 正文 回到顶部 0.前言 文章较长,而且内容相对来说比较枯燥,希望对C++对象的内存布局.虚表指针.虚基类指针等有深入了解的朋友可以慢慢看.本文的结论都在 ...

  7. C++构造函数详解(复制构造函数)

    构造函数是干什么的 该类对象被创建时,编译系统对象分配内存空间,并自动调用该构造函数,由构造函数完成成员的初始化工作,故:构造函数的作用:初始化对象的数据成员. 构造函数的种类 class Compl ...

  8. c++构造函数详解(转)

    c++构造函数的知识在各种c++教材上已有介绍,不过初学者往往不太注意观察和总结其中各种构造函数的特点和用法,故在此我根据自己的c++编程经验总结了一下c++中各种构造函数的特点,并附上例子,希望对初 ...

  9. 转 C++拷贝构造函数详解

    C++拷贝构造函数详解 一. 什么是拷贝构造函数 首先对于普通类型的对象来说,它们之间的复制是很简单的,例如: int a = 100; int b = a; 而类对象与普通对象不同,类对象内部结构一 ...

随机推荐

  1. HashMap原理阅读

    前言 还是需要从头阅读下HashMap的源码.目标在于更好的理解HashMap的用法,学习更精炼的编码规范,以及应对面试. 它根据键的hashCode值存储数据,大多数情况下可以直接定位到它的值,因而 ...

  2. Java经典编程题50道之十

    一球从100米高度自由落下,每次落地后反跳回原高度的一半:再落下……求它在第10次落地时,共经过多少米?第10次反弹多高? public class Example10 {    public sta ...

  3. PHP页面间的参数传递

    我们定义page01.php和page02.php两个php文件,将page01中的内容想办法传递到page02,然后供我们继续使用.--------------------------------- ...

  4. HDU - 2102 A计划 (BFS) [kuangbin带你飞]专题二

    思路:接BFS判断能否在限制时间内到达公主的位置,注意如果骑士进入传送机就会被立即传送到另一层,不会能再向四周移动了,例如第一层的位置(x, y, 1)是传送机,第二层(x, y, 2)也是传送机,这 ...

  5. hbase存储优化

    1.上面的2张图主要说明hbase的存储特点 (1).每个值(每条记录的每一个列的值)的存储,都完整的存储了rowkey.column family.column.版本(时间戳),以及该列的值. 这样 ...

  6. ActiveMq笔记3-AMQ高可用性理论

    单点的ActiveMQ作为企业应用无法满足高可用和集群的需求,所以ActiveMQ提供了master-slave.broker cluster等多种部署方式,但通过分析多种部署方式之后我认为需要将两种 ...

  7. js中的Object.defineProperty()和defineProperties()详解

    ECMAS-262第5版在定义只有内部采用的特性时,提供了描述了属性特征的几种属性.ECMAScript对象中目前存在的属性描述符主要有两种,数据描述符(数据属性)和存取描述符(访问器属性),数据描述 ...

  8. vs2017密钥

    Enterprise: NJVYC-BMHX2-G77MM-4XJMR-6Q8QF Professional: KBJFW-NXHK6-W4WJM-CRMQB-G3CDH

  9. LVDS/DVI/HDMI Interface

    数字视频信号 以SXGA为例,其时序如下: 垂直:         水平: 图中DSPTMG为使能信号,VSYNC为场同步信号,HSYNC为行同步信号.在行场的消隐期(T1与T7),DSPTMG为低电 ...

  10. 笔记︱支持向量机SVM在金融风险欺诈中应用简述

    本笔记源于CDA-DSC课程,由常国珍老师主讲.该训练营第一期为风控主题,培训内容十分紧凑,非常好,推荐:CDA数据科学家训练营 欺诈一般不用什么深入的模型进行拟合,比较看重分析员对业务的了解,从异常 ...