类与对象(二) 构造函数与析构函数

目录

1.类的6个默认成员函数

2.构造函数

2.析构函数


1.类的6个默认成员函数

我们前面讲到了一个空类,也就是类里面没有声明成员,但是空类里面真的什么都没有吗?不然,任何类在什么都不写时,编译器自动生成以下六个默认成员函数:

默认成员函数:默认成员函数是一种特殊的成员函数,如果我们不写,编译器就会自动生成,如果我们写了,编译器就不会生成。

对于有些类,这几个成员函数需要我们自己去实现,而对于另外一些类,编译器自动生成的就够了。这六个函数里面最重要的就是前四个,后面两个绝大多数情况下并不需要我们去实现。

2.构造函数

构造函数这个名字听起来像是用来创建对象的,但是实际上,构造函数的功能是在创建一个对象时对其初始化。

为什么C++会有构造函数呢?

我们用C语言实现数据结构时,每一个数据结构变量创建之后,第一件事就是要调用他的初始化函数,如果我们忘记初始化了,里面的值就是一些随机值,在使用的时候就会出问题。既然每一个结构体创建都要初始化,那么我们能不能把它设计成自动调用的呢?

构造函数是一个特殊的成员函数,他的名字虽然叫构造,但是他的主要任务并不是开空间创建对象,而是对创建好的对象初始化,他的功能和我们的 init 一样,但是他会在实例化对象的时候自动调用。

构造函数有以下几个特征:

1.函数名与类名相同

2.无返回值

3.对象实例化的时候编译器自动调用对应的构造函数

4.构造函数可以重载

构造函数在创建类类型对象时由编译器自动调用,以保证每一个数据成员都有一个合适的初值,并且在对象整个生命周期内只调用一次。

比如我们的日期类,我们可以自己写一个构造函数出来

class Date
{
public:Date(int year, int month, int day){_year = year;_month = month;_day = day;}private:int _year;int _month;int _day;
};

注意,构造函数一定要设置为公有的,因为编译器是在创建对象的时候自动调用,也就是在类外调用。那么我们如何在创建对象的时候给他传参呢?

	Date d(2022, 4, 23);

我们在创建对象的时候,在对象名后面加一个括号,然后在括号内传构造函数的参数,这时候我们就将对象初始化为我们传的参数了。

构造函数也是可以声明和定义分离的,这时候在函数定义的时候要指定类域。

但是我们上面写的构造函数有没有问题呢?我们有时候在创建对象的时候并不像对其传参怎么办?或者说我们并不想传三个参数,只想初始化年和月或者只想初始化年,

	Date d(2022, 4);Date d(2022);Date d;

这时候我们只有一个构造函数,因为我们自己写了构造函数了,所以编译器也不会自动生成默认的自动构造函数了,那么这三种创建对象的方法就是错误的吗?最离谱的是, Date d 竟然也不行,这是不是很滑稽

这时候,我们可能就会想到上面讲的构造函数的特性 : 支持重载,那么我们是不是要把这几种情况都列出来呢? 这样未免实现起来过于复杂了。

在C++基础入门的时候我们讲了缺省参数,那么我们是不是能在构造函数这里也用缺省参数来实现,那么是全缺省还是版缺省呢?结合我们上面的问题,半缺省的情况下还是会出现说初始化不传参的时候报错,所以我们最好是给全缺省,这样更能满足实际需求,而缺省值我们怎么给就取决于类的功能和个人的实现了。

于是我们就能给出这样的一个改良之后的构造函数

	Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}

这时候不管有没有传参,我们的构造函数都是能调用的。

构造函数我们也是可以 f11 进入到函数里面调试的。

这里大家可能会有一个问题,为什么上面的无参构造不用括号呢?

Date d;
//无参构造为什么不是这样呢?
Date d();

我们可以看到,带括号的形式看起来是不是就是一个函数的声明,Date 是返回类型, d是函数名,括号和里面就是参数列表。 如果无参构造也是这样写的话,不要去也会分不清到底是声明还是构造。而且,无参的时候不加括号更符合我们的逻辑。

而如果我们不自己实现构造函数,编译器自动生成的默认成员函数是怎么样的呢?

我们可以把上面的代码注释掉来观察一下

当我们走到return这一行来了,d的成员变量还是随机值,这是为什么呢?编译器自动生成的构造函数什么也没干啊,这些变量还是随机值啊,那他自动生成的构造函数有什么意义呢?

C++把类型分成内置类型(基本类型)和自定义类型,内置类型就是语言提供的 int 、char 等等,自定义类型就是我们用struct ,class,onion等自己定义的类型。

编译器自动生成的构造函数,对于内置类型不处理,但是对于自定义类型,他会去调用这个自定义类型的构造函数。

为什么编译器对内置类型不做处理呢?因为编译器也不清楚这些变量是用来做什么的,不知道要给他们什么样的初始值才合适,不管怎么样总有情况不会满意,所以编译器也不敢处理内置类型。

所以说,自动生成的默认构造函数还是会做事情的,只是对内置类型不处理。

对于自定义类型,自动生成的构造函数会去调用该类型的构造函数,这个怎么理解呢?

我们可以拿之前的用栈实现队列来举例,我们的队列使用两个栈来完成的。

class Stack
{int* _a;int _top;int _capacity;
};class MyQueue
{Stack pushST;Stack popST;
};

队列的成员变量是自定义类型Stack对象,那么对于MyQueue 类,编译器自动生成的构造函数他会去调用 Stack 的构造函数对这两个成员变量初始化。我们可以把栈的构造函数写出来测试一下MyQueue 编译器自动生成的构造函数是否真的会去调用Stack的构造函数

我们在Stack的构造函数中打印一行提示信息出来,我们就能发现,用MyQueue类来实例化对象的时候,它调用了两次Stack的构造函数,因为他的成员里面有两个Stack对象。通过监视也可以看到他确实使用Stack的构造函数初始化了

这时候我们就能大概想明白,什么时候需要我们自己写构造函数,什么时候编译器自动生成的构造函数就够用了,对于MyQueue这样的成员全是自定义类型变量的的类,我们就不用自己实现构造函数,因为编译器自动生成的构造函数就回去调用他们的构造函数。

那么如果我们再给MyQueu类中加一个 size 成员呢?

class MyQueue
{Stack pushST;Stack popST;int _size;
};

这时候当我们再使用编译器自动生成的构造函数时,就会发生这样的情况

我们这里的 vs 编译器的情况不是很理想,因为C++对于内置标准就是对于内置类型不处理,而vs编译器有点多管闲事了。 正常情况下,q 对象应该会是 初始化了 两个栈,但是size仍然是随机值,那么这样是不是很尴尬,那么我们是不是就要自己实现MyQueue的构造函数了?MyQueue的构造函数实现起来,既要实现Stack的构造,又要初始化size是不是很麻烦?

我们有两种方法针对这种既有内置类型又有自定义类型的类

1.对于内置类型,我们可以在声明时给缺省值,也就是默认值。C++中针对内置类型不初始化的缺陷,又打了个补丁,就是内置类型成员变量在类中声明时可以给缺省值,注意不是对成员变量初始化,因为声明是没有空间的,是给成员变量一个默认值,如果初始化时对该成员变量进行操作,该成员变量就会使用在这个缺省值。我们将MyQueue修改成这种形式:

class MyQueue
{Stack pushST;Stack popST;int _size=0;
};

这样一来,我们不用写构造函数,MyQueue 的自动生成的构造函数对于两个栈就回去调用他们的的构造函数初始化,而size 就会使用他的缺省值。

同时,我们上面的日期类也可以这样给缺省值,这样一来我们也不用自己写构造函数了。其实,上面栈也是可以的,这个缺省值是很牛逼的。我们不仅能给指定的缺省值,还能用缺省值 malloc开辟空间。

class Stack
{
private:int* _a=(int*)malloc(sizeof(int)*4);int _top=0;int _capacity=4;
};

这里的 malloc 在类的声明中并没有开辟空间,而是相当于打了一个样,而是在创建对象的时候,构造函数如果没有对 _a 进行初始化,_a 就会用这个缺省值。 但是这个缺省值会有一个问题, 就是不能检查malloc 的返回值是否为空指针。

2.初始化列表,初始化列表我们会放在类和对象的后期去讲。

默认构造函数

无参的构造函数和全缺省的构造函数都成为默认构造函数,并且默认构造函数有且只能有一个(避免二义性,无参和全缺省调用的时候会冲突),类中如果有多个默认构造函数,编译器就会报错。

并不是说只有编译器自动生成的构造函数才是默认构造函数,我们自己实现的无参的或者全缺省的构造函数也是默认构造,编译器自动生成的就是无参的。总的来说,不传参数就能调用的构造函数就是默认构造函数。

所以我们在声明一个类的时候,最好要有一个默认构造函数,为了防止我们创建对象时不传参数的情况。

注意:如果一个类里面包含其他类类型成员,就比如上面的MyQueue 中的pushST和popST,这些自定义类型成员必须有默认构造函数也就是不传参的构造函数,因为编译器自动生成的 MyQueue 的默认构造函数在初始化 popST和pushST 的时候是去调用他们的无参的构造函数。

到这里构造函数我们就先讲这么多,其实构造函数还有一些其他的知识点我们在这里没讲,放到了后面我们来补充。

2.析构函数

析构函数:与构造函数功能相反,析构函数并不是完成对对象本身的销毁,这些对象也就是局部变量的销毁工作是由编译器完成的,当函数栈帧销毁时对象自然就跟着一起销毁了。析构函数完成的是对象中的资源的清理工作,清理工作就跟我们以前写的destroy相对应,他不是销毁对象本身,而是释放对象中的资源,我们目前所知道的资源就是 动态开辟的空间 以及 文件缓冲区,析构函数要做的就是释放动态开辟的内存以及关闭文件。析构函数是在对象销毁时由编译器自动调用的。

析构函数的特征:

1.析构函数名就是类名前面加 ~ (~就是我们C语言学的 按位取反的符号,意思是与构造函数的功能相反)。

2.析构函数是午餐无返回类型的

3.一个类只能有一个析构函数,若未显示定义,系统会自动生成默认的系统函数,所以析构函数是不能重载的。

4.对象生命周期结束时,C++编译器会自动调用析构函数

自动生成的析构函数与我们的构造函数是类似的,对于内置类型他不会处理,对于自定义类型会去调用他的析构函数。

那么上面的日期类我们就不需要自己写析构函数,因为他没有资源需要释放,可能有人会说我们不用把成员变量的值还原成初始化的样子吗?我们要知道,析构函数是在对象销毁之前的那一刻自动调用的,对象都要销毁了,对于这些临时变量它的值修补修改都是一样的。

而对于Stack类,因为我们动态开辟了一个数组,所以我们是要自己去写他的析构函数的,析构函数的功能就是释放掉动态开辟的数组,_a 是否置为nullptr 都无所谓。

~Stack(){free(_a);}

而对于MyQueue类,他虽然有资源需要释放,但是资源都是在pushST和popST中的,编译器自动生成的析构函数会去调用Stack的析构函数去释放资源,所以我们是不用自己去想实现MyQueue的析构函数的。

要不要自己实现析构函数就一个原则: 如果编译器自动生成的就能释放掉资源,我们就不用自己写,免得画蛇添足。如果编译器自动生成的不能满足需求,我们就要自己写一个析构函数。

析构函数和构造函数是功能相反的一对默认成员函数,但是他们的特点是相似的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/2293.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Introducing Meta Llama 3: The most capable openly available LLM to date

要点 今天,我们推出 Meta Llama 3,这是我们最先进的开源大型语言模型的下一代。Llama 3型号将很快在AWS,Databricks,Google Cloud,Hugging Face,Kaggle,IBM WatsonX,Microsoft Azur…

VsCode一直连接不上 timed out

前言 前段时间用VsCode连接远程服务器,正常操作后总是连接不上,折磨了半个多小时,后面才知道原来是服务器设置的问题,故记录一下,防止后面的小伙伴也踩坑。 我使用的是阿里云服务器,如果是使用其他平台服务…

CCS项目持续集成

​ 因工作需要,用户提出希望可以做ccs项目的持续集成,及代码提交后能够自动编译并提交到svn。调研过jenkins之后发现重新手写更有性价比,所以肝了几晚终于搞出来了,现在分享出来。 ​ 先交代背景: 1. 代码分两部分&am…

IPRally巧用Google Kubernetes Engine和Ray改善AI

专利检索平台提供商 IPRally 正在快速发展,为全球企业、知识产权律师事务所以及多个国家专利和商标局提供服务。随着公司的发展,其技术需求也在不断增长。它继续训练模型以提高准确性,每周添加 200,000 条可供客户访问的可搜索记录&#xff0…

Linux之 USB驱动框架-USB总线(2)

一、linux 下,通过系统查看usb 总线 ls /sys/bus/usb/devices/也包含了很多信息: 其中usb1、usb2代表系统注册了2条usb总线,即有2个USB主机控制器,1和2用于区分不同总线,是USB的总线号。 每插入一个usb设备&#xff…

深度学习-数据操作

目录 张量通过shape属性访问张量的形状通过shape属性访问张量中元素的总数reshape改变张量的形状(不改变元素数量和元素值)使用全0、全1、其他常量或者从特定分布中随机采样的数字通过提供包含数值的Python列表为所需张量中的每个元素赋予确定值。张量的…

半导体存储器整理

半导体存储器用来存储大量的二值数据,它是计算机等大型数字系统中不可缺少的组成部分。按照集成度划分,半导体存储器属于大规模集成电路。 目前半导体存储器可以分为两大类: 只读存储器(ROM,Read Only Memory&#xff…

如何判断客户需求能不能做出来产品?

在做G端产品的过程中,为了让产品可以符合客户实际需求,我们需要经历客户需求调研的这个环节。那么,需求收集后,我们要从什么维度判断客户的需求是否真的可以产品化呢? 我们做G端产品,新产品的方向几乎100%来自于政策。所以才会有“政策带来产品,产品催生政绩”。 可就算…

解锁ApplicationContext vs BeanFactory: 谁更具选择性?

目录 一、聚焦源码回顾 (一)源码分析和理解 (二)简短的回顾对比建议 二、ApplicationContext vs BeanFactory特性对比 (一)主要特性总结 (二)直接建议 三、案例简单说明 &am…

OpenTelemetry-1.介绍

目录 1.是什么 2.为什么使用 OpenTelemetry 3.数据类型 Tracing Metrics Logging Baggage 4.架构图 5.核心概念 6.相关开源项目 ​编辑 7.分布式追踪的起源 8.百花齐放的分布式追踪 Zipkin Skywalking Pinpoint Jaeger OpenCensus OpenTracing 9.Openteleme…

虚假新闻检测——Adapting Fake News Detection to the Era of Large Language Models

论文地址:https://arxiv.org/abs/2311.04917 1.概论 尽管大量的研究致力于虚假新闻检测,这些研究普遍存在两大局限性:其一,它们往往默认所有新闻文本均出自人类之手,忽略了机器深度改写乃至生成的真实新闻日益增长的现…

【北京迅为】《iTOP-3588开发板系统编程手册》-第20章 socket 应用编程

RK3588是一款低功耗、高性能的处理器,适用于基于arm的PC和Edge计算设备、个人移动互联网设备等数字多媒体应用,RK3588支持8K视频编解码,内置GPU可以完全兼容OpenGLES 1.1、2.0和3.2。RK3588引入了新一代完全基于硬件的最大4800万像素ISP&…

TI_DSP_F2808学习笔记1: GPIO

1. 初始化设置 1.1 控制寄存器 GPxMUX功能选择寄存器/多功能复用选择 GPxDIR 方向选择寄存器/输入输出选择 0 输入 1 输出GPxPUD 上拉功能选择寄存器/是否启用内部上拉 0 有上拉,1禁止上拉GPxQSeln输入限定选择寄存器 输入n次为0或1才有效,滤波 1.2 数…

PDF 书签制作与调整

本文是对以前发表的旧文拆分,因为原文主题太多,过长,特另起一篇分述。 第一部分 由可编辑 PDF 文档创建书签 方法 1. Adobe Acrobat Pro autobookmark AutoBookmark 是一个可用于 Adobe Acrobat 自动生成书签的插件。 官方下载地址&…

corona渲染器锐化模糊设置,corona高效出图方法

​在使用Corona渲染器进行效果图渲染时,锐化和模糊是两种常用的设置,它们主要用于调整图像的清晰度和柔化效果。锐化参数可以增强图像中的细节,使画面看起来更加清晰锋利;而模糊参数则可以用来柔化图像边缘,减少图像噪…

Etsy多账号关联怎么办?Etsy店铺防关联解决方法

Etsy虽然相对于其他跨境电商平台来说比较小众,但因为平台是以卖手工艺品为主的,所以成本较低,利润很高。许多跨境卖家都纷纷入驻,导致平台规则越发严格,操作不当就会封号,比如一个卖家操作多个账号会出现关…

10.接口自动化测试学习-Pytest框架(2)

1.mark标签 如果在每一个模块,每一个类,每一个方法和用例之前都加上mark标签,那么在pytest运行时就可以只运行带有该mark标签的模块、类、接口。 这样可以方便我们执行自动化时,自主选择执行全部用例、某个模块用例、某个流程用…

二分查找知识点及练习题

知识点讲解 一、没有相同元素查找 请在一个有序递增数组中(不存在相同元素),采用二分查找,找出值x的位置,如果x在数组中不存在,请输出-1! 输入格式 第一行,一个整数n,代…

家用洗地机买什么牌子的好?四大业内顶尖品牌推荐

家庭清洁一直是必不可少的,但用传统的手动拖地清洁,费时又费力。现在出现了洗地机,确实改变了我们对家庭清洁的看法。它不仅能扫地、拖地,还能吸水,甚至能够自动清洁滚刷解放我们双手,提供高效清洁的同时还…

【Linux系列】 离线安装vnc 可视化桌面

离线安装vnc 可视化桌面 缘下载安装vnc初始化链接 缘 项目需要下载 下载地址: http://mirror.centos.org/centos/7/updates/x86_64/Packages/tigervnc-license-1.8.0-31.el7_9.noarch.rpm http://mirror.centos.org/centos/7/os/x86_64/Packages/libXfont2-2.0.…