造化从来自有神

如何对此亦无尘

平生出处皆非妄

老去功名始见真

这是小编以“构造对象”为主题让九歌同学创作的七言绝句。九歌同学以一种非常玄妙的文风向我们介绍了对象的构造,但这似乎并不是我们想要的答案。那么我们每次码代码都在进行的“对象构造”究竟是怎样一回事呢?就让小编带领大家来了解一下吧!

什么是对象的构造?

对象的构造过程就是对象的创建过程。在对象构造的过程中,我们需要进行对象数据成员的初始化,因此对象构造的关键问题就是要解决对象的成员如何初始化的问题。

C++中是如何解决对象成员初始化问题的?

我们可以对照着C语言中结构体数据成员初始化的解决方案来看。

假定我们有如下的结构体:

C语言的程序员们有两种对其进行初始化的方法。

其一是顺序初始化:

其二是指定初始化:

我们可以注意到,使用顺序初始化方法往往不够灵活,需要逐一列出所有成员的初始值;而指定初始化法不被很多C++编译器兼容,例如小编使用的Visual Studio就不兼容这种写法,会报错。

在C++中,由于成员函数的引入,我们可以在类中定义一个成员函数用来进行类的初始化。但这样会带来一个问题,假如在对象被创建之后我们忘记调用这个成员函数,那么对象数据成员的值将是不确定的,对于一个大型的工程来说,这可能造成很大的麻烦。这是由于对象的创建以及成员的初始化分离了。此外,对于一些必须进行初始化的成员,例如const类型和引用类型的成员,它们的初始化需要和对象同步进行,这种先创建对象在初始化成员的做法也会带来问题。

构造函数——千呼万唤始出来

•  构造函数的一般特征

构造函数是一类特殊的成员函数。相较于一般的成员函数,构造函数有这样两个显著的特征:

1、所有构造函数都没有返回类型的声明;

2、构造函数仅在对象被创建时被调用。

我们来看一个最简单的构造函数的例子:

在这个例子中,构造函数被调用时,会首先创建A这个对象(此时a中的数据成员尚未被赋值),然后执行构造函数的函数体,将A的数据成员a赋值为1,并输出相关语句。我们可以观察到构造函数的一些基本特征:没有返回类型的声明,且函数名称与类名相同。

  • 多种类型的构造函数

C++中对象被创建的方式是多种多样的,因此构造函数的形式也是多种多样的。接下来就让我们看一看为了完成对象创建这个任务,都有哪些类型的构造函数吧!

默认构造函数:

当且仅当类中没有定义构造函数时,编译器会提供一个默认的构造函数,这个构造函数没有参数,并且函数体也是空的。在上面的例子中,这个构造函数就类似于:Test(){};

无参构造函数:

上面讲到的默认构造函数就是一种无参构造函数。需要注意的是,如果参数表中的形参均具有默认值,此时我们也可以将它当做无参的构造函数来调用:

有参构造函数:

我们通过一个例子来感受一下有参构造函数的使用以及初始化列表的作用。

在这个例子中,我们声明的类Test有三个数据成员,其中有一个是引用成员,一个是const类型的成员,这两种类型的数据成员在定义时就必须进行初始化,因此我们不能在构造函数的函数体内将它们初始化,而应当在初始化列表中将它们初始化,这是因为在初始化列表中进行的初始化工作是在对象创建完成之前进行的,而在函数体中进行初始化是在对象创建之后进行的。

一般来说声明了带参数的构造函数之后我们最好还要定义一个默认构造函数,以防止系统编译时出错。

拷贝构造函数——我来组成一支大军!

•为什么需要拷贝构造函数?

对于普通类型的变量而言,变量的复制是容易的,利用一个变量来初始化一个新变量也是容易的:

但正如对象的创建需要构造函数一样,对象内部因为有各种成员变量,其复制过程也比普通的变量要复杂,因此通过复制的方法使用一个对象去初始化另一个对象就需要用到拷贝构造函数。

我们看一个简单的例子:

在这个例子中,以“const Test& t”为参数的函数就是一个简单的拷贝构造函数。拷贝构造函数的一般特征是:

1、参数中必须含有本类对象的一个引用,可以含有其他参数,但这些参数必须有默认值;

2、函数名与类名相同,且没有返回类型的声明。

因为使用了引用,且拷贝构造函数一般不希望改变被复制的对象,因此我们往往在这个参数前加上“const”修饰符。

  • 拷贝构造函数的调用时机

拷贝构造函数在下3种情况下会被调用:

1、用类的对象初始化类的另一个对象:在上面举出的例子中我们可以看到,当执行

Test B=A;

时,拷贝构造函数会被调用。此外,如果我们把这个语句改写成:

Test B(A);

拷贝构造函数也会被调用。这两种写法执行的操作完全相同。

2、某个函数的形参是类的对象,当这个函数被调用,进行形实结合时也会调用拷贝构造函数:

当调用函数 f(Test a) 时,会调用Test类的拷贝构造函数。只有参数使用值传递时才会调用拷贝构造函数,使用引用传递则不会,因此在传递较大的对象时,一般采用引用传递的策略。

3、函数的返回值是类的对象,在返回值时会调用拷贝构造函数。例如:

在这个例子中,执行语句

b=f(A);

时,当传递参数时拷贝构造函数不会被调用(因为使用的是引用传递),但函数返回对象时拷贝构造函数会被调用。

  • 深拷贝和浅拷贝

当我们定义一个类时,系统一般会给出一个默认的拷贝构造函数,这个构造函数可以实现成员对象简单的一一复制。如果成员中没有动态成员的话,这个一一复制的结果就是我们想要的,但如果成员中有一个指针指向了一片动态开辟的存储空间,使用系统默认的拷贝构造函数就会出现问题:

假设我们定义的类Test中有一个指针变量p,且我们先创建了一个对象A。A中的指针p在对象使用的过程中指向的是一片动态开辟的存储空间,那么在我们执行语句

Test B=A;

时,B中的指针p也会指向同一片存储空间。

假设我们在析构函数的函数体中试图释放这片存储空间,那么由于B和A中的指针p指向了同一片空间,而同一片空间不可以被释放两次,系统就会报错。(关于析构函数的内容,可以参考文章的下一部分)

同样的,假如B和A的指针指向的存储空间是一样的,那么A.p指向的内容改变的话,B.p指向的内容也会被改变,这显然不是我们想要的。

因此我们想要实现的是,拷贝过程中,A和B中的动态变量指向的内容相同,但指针值本身不同。这就是所谓的“深拷贝”。

我们可以考虑以下的方式来定义一个深拷贝函数:

在这个例子中,拷贝构造函数的函数体使得复制出来的对象的指针p指向的内容与被复制对象的指针p指向的内容一致,但两者本身指向的存储空间是不同的,也就实现了深拷贝。

~析构函数——我有神奇小尾巴~

•初识析构函数

类的析构函数,它是类的一个成员函数,名字由波浪号加类名构成,是执行与构造函数相反的操作:释放对象使用的资源,并销毁非static成员。

析构函数有这样几个特征:

1.函数名是在类名前加上~,无参数且无返回值,因此析构函数不能重载。

2.一个类只能有且有一个析构函数,如果没有显式的定义,系统会生成一个缺省的析构函数。

我们看一个简单的析构函数的例子:

在讨论构造函数时我们提到过,构造函数的函数体是在对象初始化完成之后执行的,这也是某些数据类型的成员不能通过函数体进行初始化的原因。与构造函数相反,析构函数的函数体是在对象被销毁前执行的,并且成员的销毁顺序也是与初始化的顺序相反的。

  • 释放资源?

我们知道析构函数的作用是销毁不再使用的对象(好残忍),释放系统资源,那么析构函数是怎么做到这一点的呢?

其实小编也不知道 如果是对象中非指针类型的成员,那么在执行析构函数之后,这些成员占用的内存空间会被释放;但有一类情形是,假如对象中有指针类型的成员,并且它指向的是一块动态开辟的存储空间,那么这块空间必须由我们手动释放,也就是说需要在析构函数的函数体中释放这块存储空间。

小编弱语

关于对象的构造和析构,其实还有很多可聊的,但由于小编实力有限,暂时只能和大家聊这么多啦!最后,献上九歌同学的一首五言绝律诗《你在哪里啊,我的对象》,向大家告别~

你在哪里啊,我的对象?

九歌

一自伤春眼,无人识此心
山林随日转,云雨隔年沉
世路皆新鬼,浮生付乱禽
凭君莫剪伐,吾已负初音

文字 | 周航

审核|刘政宁

指导老师|李超

对象 普通po转_谈谈C++对象的构造相关推荐

  1. .net new一个类为什么报空指针_谈谈.NET对象生命周期

    不用程序员操心的堆 - 托管堆 程序在计算机上跑着,就难免会占用内存资源来存储在程序运行过程中的数据,我们按照内存资源的存取方式将内存划分为堆内存和栈内存. 栈内存通常使用的场景是:对存取速度要求较高 ...

  2. python提供两个对象身份比较操作符_标准类型对象比较操作符

    标准类型对象比较操作符 (2008-08-31 12:22:17) 标签: it 标准类型对象身份比较操作符 Python 提供了is和is not操作符来测试两个变量是否指向同一个对象. 像下面这样 ...

  3. java 对象引用 弱引用吗_谈谈Java对象的强引用,软引用,弱引用,虚引用分别是什么...

    整体结构 java提供了4中引用类型,在垃圾回收的时候,都有自己的各自特点. 为什么要区分这么多引用呢,其实这和Java的GC有密切关系. 强引用(默认支持模式) 把一个对象赋给一个引用变量,这个引用 ...

  4. 前端判断session对象是否为空_谈谈JavaScript这个语言与前端是否需要面向对象?...

    事实上,一个语言是否流行与它是否优秀没有任何关系.JS绑架了整个Web前端,而我们又没有其他的选择. --题记 最近这两年用JavaScript用的比较频繁.一是2017年时开始制作小程序课程:二是2 ...

  5. 对象 普通po转_厦门2020年转学怎么转?需要什么材料?你想知道的答案都在这!......

    很多人都在问中小学转学的问题! 孩子转学需要符合什么条件? 转学需要注意些什么? 再说一下 目前只有海沧公布了转学安排 2020年海沧区插班生预约登记时间为: 7月4日和8月15日 其他区还没有公布 ...

  6. java 对象验证非空_判断Bean对象指定字段非空

    判断Bean对象指定字段非空. 方案: 在bean对象上增加注解,指定字段非空,返回异常信息有明确字段描述,省去不必要的if.else判断. 新建注解类 /** * 参数校验,判断字段非空.返回异常文 ...

  7. java不同对象的集合拷贝_集合java对象

    JAVA基础教程汇总 到这一篇博客为止,JAVA的基础部分已经介绍完了,这一篇为基础部分的汇总. JAVA介绍 JAVA概述 Java的特点 JDK/bin目录下的不同exe文件的用途 JAVA环境变 ...

  8. 移除Java对象中的属性_在java对象中添加和删除属性

    我怎样才能在 java中实现这一点.我有一个具有属性的对象. public class Object { private final Credentials Credentials; private ...

  9. java对象流保存表格_使用java对象

    <21天学通Java(第7版)>-- 1.3 对象和类 本节书摘来异步社区<21天学通Java(第7版)>一书中的第1章,第1.3节,作者: [美]Rogers Cadenhe ...

最新文章

  1. jquery中ajax请求后台数据成功后既不执行success也不执行error解决方法
  2. 【计算理论】计算复杂性 ( 非确定性图灵机的时间复杂度 | 非确定性图灵机 与 确定性图灵机 的时间复杂度 之间的关系 )
  3. 阿联酋是发达国家还是发展中国家
  4. 合成模式(Composite Pattern)
  5. MySQL的行转列、列转行、连接字符串 concat、concat_ws、group_concat函数用法
  6. android基础入门思考题答案,《Android移动应用基础教程》中国铁道出版社课后习题(附答案)...
  7. 栈的顺序存储结构框架搭建
  8. highcharts
  9. hdu1233 还是畅通工程 基础最小生成树
  10. ROS2.9.27架设网吧软路由实战篇之端口映射与回流
  11. 微信接口开发,config提示OK,但分享不成功
  12. 带你破解时间管理的谜题
  13. [周更]5.unity3D 三体运动(简易版)
  14. 二叉树节点x所在的层数
  15. 如何用Python将Word文档转换为Excel表格
  16. MODBUS报文负数优化处理代码(补码,反码) java
  17. Prometheus + Grafana 图形化监控实践
  18. python 手机自动化交易股票_通达信转python,机智股票自动交易手机版
  19. 【SAP Abap】SAP增强开发总结
  20. MATLAB tabulate函数涉及正整数的统计bug

热门文章

  1. 商城项目-商品添加功能实现
  2. mysql存储过程在实际开发中怎么用_MYSQL存储过程开发中怎么使用游标嵌套
  3. 【已解决】Jsp实现文件上传功能
  4. 8086 DS 和 ES 寄存器
  5. 小朋友你是否对java中的static满脸问号
  6. 小程序获取sessionkey_小程序,足不出户获取更多客源
  7. python中的import详解_python中的import
  8. 手机版ziperello_Ziperello
  9. 京东的商品搜索功能是如何实现的_【干货小知识】京东商家如何优化搜索流量?...
  10. UML 类图几种关系的总结