大家都定义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. ASP.NET Core 2.0 : 五.服务是如何加载并运行的, Kestrel、配置与环境

    "跨平台"后的ASP.Net Core是如何接收并处理请求的呢? 它的运行和处理机制和之前有什么不同? 本章从"宏观"到"微观"地看一下它的 ...

  2. 工作笔记--自动切换host的python code

    修改host代码: #coding:utf-8import os,timepwd = os.path.dirname(__file__) #获取当前文件夹的绝对路径pull_host_cmd = 'a ...

  3. 让互联网更快:新一代QUIC协议在腾讯的技术实践分享

    本文来自腾讯资深研发工程师罗成在InfoQ的技术分享. 1.前言 如果:你的 App,在不需要任何修改的情况下就能提升 15% 以上的访问速度,特别是弱网络的时候能够提升 20% 以上的访问速度. 如 ...

  4. MysqL读写分离的实现-Mysql proxy中间件的使用

    为什么要架设读写分离,这里不做多余的说明,想了解具体原理,请百度或者参考其他帖子.在这里只做大概的配置说明,测试中使用三台服务器 192.168.136.142   主服务器 192.168.136. ...

  5. Git版本回退和撤销修改的区别

    在阅读廖雪峰git教程时,对版本回退和暂存区撤销修改没太看懂,所以自己测试了一下. 版本回退: git reset --hard HEAD 这个命令用于版本回退,就是将已提交的版本覆盖本地工作区的内容 ...

  6. Qt Create or VS 2015 使用 Opencv330 相机静态库链接错误如何解决?

    查看链接库,添加 vfw32.lib 即可.

  7. B. Pyramid of Glasses

    原题链接 B. Pyramid of Glasses Mary has just graduated from one well-known University and is now attendi ...

  8. Ubuntu搭建Hadoop的踩坑之旅(一)

    本文将介绍如何使用虚拟机一步步从安装Ubuntu到搭建Hadoop伪分布式集群. 本文主要参考:在VMware下安装Ubuntu并部署Hadoop1.2.1分布式环境 - CSDN博客 一.所需的环境 ...

  9. JavaScript操作符汇总

    操作符 JavaScript 有赋值.比较.算术.位.逻辑.字符串和特殊运算符.本章描述了操作符,以及关于操作符优先级的一些信息. 表 2.1 JavaScript 所有操作符简明列表. 表 2.1 ...

  10. 关于 target="_blank"漏洞的分析

    创建: 于 八月 30, 2016 关于 target="_blank"漏洞的分析  一.漏洞详情:首先攻击者能够将链接(指向攻击者自己控制的页面的,该被控页面的js脚本可以对母页 ...