【C++心愿便利店】No.5---构造函数和析构函数

文章目录

  • 前言
  • 一、类的6个默认成员函数
  • 二、构造函数
  • 三、析构函数


前言

在这里插入图片描述

👧个人主页:@小沈YO.
😚小编介绍:欢迎来到我的乱七八糟小星球🌝
📋专栏:C++ 心愿便利店
🔑本章内容:类和对象
记得 评论📝 +点赞👍 +收藏😽 +关注💞哦~


提示:以下是本篇文章正文内容,下面案例可供参考

一、类的6个默认成员函数

如果一个类中什么成员都没有,简称为空类。
空类中真的什么都没有吗?并不是,任何类在什么都不写时,编译器会自动生成以下6个默认成员函数。
默认成员函数:用户没有显式实现,编译器会生成的成员函数称为默认成员函数
请添加图片描述

二、构造函数

1. 构造函数的概念

如下Date类,没有初始化打印出来就会是随机值,同时对于栈没有初始化,就会报错
那如果想能否在创建对象的同时,就将信息设置进去呢。因此,就有了构造函数。以Date类为例:

#include<iostream>
using namespace std;
class Date
{
public:void Init(int year, int month, int day){_year = year;_month = month;_day = day;}void Print(){cout << _year << "/" << _month << "/" << _day << endl;}
private:int _year;int _month;int _day;
};
int main()
{Date d1;d1.Print();//没有调用Init初始化函数Date d2;d2.Init(2022, 7, 6);//调用Init初始化函数d2.Print();return 0;
}

请添加图片描述

对于Date类,可以通过 Init 公有方法给对象设置日期,但如果每次创建对象时都调用该方法设置信息,未免有点麻烦,那能否在对象创建时,就将信息设置进去呢?

2. 构造函数的定义

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

3. 构造函数的特性

构造函数是特殊的成员函数,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任务并不是开空间创建对象,而是初始化对象
其特征如下:

  • 🌏函数名与类名相同。
  • 🌏无返回值。(不需要写void)
  • 🌏对象实例化时编译器自动调用对应的构造函数。

对于上述代码所运行后的结果没有初始化d1结果是随机值,然后对比下述代码(同样没有初始化d1)及结果;运行结果自动初始化为1/1/1不是随机值且打印了Date(),这就说明对象实例化时编译器自动调用对应的构造函数

#include<iostream>
using namespace std;
class Date
{
public:构造函数Date()函数名与类名相同且无返回值{cout << "Date()" << endl;_year = 1;_month = 1;_day = 1;}void Init(int year, int month, int day){_year = year;_month = month;_day = day;}void Print(){cout << _year << "/" << _month << "/" << _day << endl;}
private:int _year;int _month;int _day;
};
int main()
{Date d1;d1.Print();return 0;
}

请添加图片描述

  • 🌏构造函数可以重载。(本质可以写多个构造函数,提供多种初始化方式)
#include<iostream>
using namespace std;
class Date
{
public:1. 无参的构造函数Date(){}也可以写成下面这种Date()//函数名与类名相同且无返回值{cout << "Date()" << endl;_year = 1;_month = 1;_day = 1;}2. 有参的构造函数Date(int year, int month, int day){_year = year;_month = month;_day = day;}3. 全缺省的构造函数Date(int year=1, int month=1, int day=1)无参和全缺省的不能同时存在会存在调用歧义{_year = year;_month = month;_day = day;}void Init(int year, int month, int day){_year = year;_month = month;_day = day;}void Print(){cout << _year << "/" << _month << "/" << _day << endl;}
private:int _year;int _month;int _day;
};
int main()
{Date d1;调用无参的构造函数//Date func();这也可以是一个函数声明所以为了区分不能加()d1.Print();Date d2(2023,8,28);调用带参的构造函数d2.Print();对于全缺省的构造函数使用更灵活可以传一个参数,两个等Date d3(2023);d3.Print();Date d4(2023, 8);d4.Print();return 0;
}

🌟注意:如果通过无参构造函数创建对象时,对象后面不用跟括号(例如Date d1() 是错误的 ),否则就成了函数声明以下代码的函数:声明了d1函数,该函数无参,返回一个日期类型的对象warning C4930: “Date d1(void)”: 未调用原型函数(是否是有意用变量定义的?)
请添加图片描述

  • 🌏如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成。
 class Date{public:/*// 如果用户显式定义了构造函数,编译器将不再生成Date(int year, int month, int day)(有参的构造函数){_year = year;_month = month;_day = day;}*/void Print(){cout << _year << "-" << _month << "-" << _day << endl;}private:int _year;int _month;int _day;};int main(){Date d1;return 0;}

将Date类中构造函数注释后,代码可以通过编译,因为编译器生成了一个无参的默认构造函数;将Date类中构造函数放开,代码编译失败,因为一旦显式定义任何构造函数,编译器将不再生成无参构造函数,放开后报错:error C2512: “Date”: 没有合适的默认构造函数可用

  • 🌏不实现构造函数的情况下,编译器会生成默认的构造函数。但是看起来默认构造函数又没什么用?d对象调用了编译器生成的默认构造函数,但是d对象_year/_month/_day,依旧是随机值。也就说在这里编译器生成的默认构造函数并没有什么用??

🌟解答:C++把类型分成内置类型(基本类型)和自定义类型。内置类型就是语言提供的数据类型,如:int/char…,自定义类型就是我们使用class/struct/union等自己定义的类型,所有类型的指针都是内置类型

#include<iostream>
using namespace std;
class Time
{
public:Time(){cout << "Time()" << endl;_hour = 0;_minute = 0;_second = 0;}
private:int _hour;int _minute;int _second;
};
class Date
{
private:// 基本类型(内置类型)int _year;int _month;int _day;// 自定义类型Time _t;
};
int main()
{Date d;return 0;
}

在这里插入图片描述
🌟注意:C++11 中针对内置类型成员不初始化的缺陷,又打了补丁,即:内置类型成员变量在类中声明时可以给默认值

#include<iostream>
using namespace std;
class Time
{
public:Time(){cout << "Time()" << endl;_hour = 0;_minute = 0;_second = 0;}
private:int _hour;int _minute;int _second;
};
class Date
{
private:// 基本类型(内置类型)这个地方不是初始化而是声明,声明给的缺省值,默认生成的构造函数就会用这个缺省值初始化int _year = 2023;int _month = 9;int _day = 5;// 自定义类型Time _t;
};
int main()
{Date d;return 0;
}

在这里插入图片描述

无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。
注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为
是默认构造函数。它们都有一个共同的特点:可以不用传参。默认构造函数只能有一个,前面两个,在语法上可以构成函数重载,但是在无参调用的时候,会发生歧义,出现调用不明确。

注意:要把默认构造函数和默认成员函数区分清楚,默认成员函数是我们不写编译器会自动生成的,默认构造函数是不需要传参的构造函数。编译器生成的构造函数,既是默认构造函数,同时也是默认成员函数。
🌟为什么上述说内置类型用的缺省值

#include<iostream>
using namespace std;
class Date
{
public:Date(){这里_year没有给值而_month _day给了值打印出来是2023/2/1所以声明那给的是缺省值_month = 2;_day = 1;}void Print(){cout << _year << "/" << _month << "/" << _day << endl;}
private:int _year = 2023;int _month = 9;int _day= 5;
};int main()
{Date d1;d1.Print();return 0;
}

在这里插入图片描述

🌟编译器生成的默认构造的特点:

  1. 不写才会生成,写了任意一个构造函数就不会生成了
  2. 内置类型也叫基本类型(语言用关键字定义的类型);内置类型的成员不会处理(根据编译器有的会有的不会)
  3. 自定义类型的成员才会处理,会去调用这个成员的构造函数

🌟总结:
一般情况下,都需要我们自己写构造函数,决定初始化方式;成员变量全是自定义类型,可以考虑不写构造函数

  • 🌏无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个多个并存在会存在调用的二义性。注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认构造函数(不传参就可以默认调用的)

三、析构函数

1. 析构函数的概念

析构函数:与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作。

2. 析构函数的特性

析构函数是特殊的成员函数,其特征如下:

  • 🌏析构函数名是在类名前加上字符 ~。
  • 🌏无参数无返回值类型。
  • 🌏一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构函数不能重载
  • 🌏对象生命周期结束时,C++编译系统系统自动调用析构函数

温馨提示:析构函数不能重载。
🌟后定义先析构

#include<iostream>
#include<assert.h>
using namespace std;
class Date
{
public:Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}~Date(){cout << "Date()" << endl;}
private:int  _year;int _month;int _day;
};
class Stack
{
public:Stack(size_t n=4){cout << "Stack(size_t n=4)" << endl;if (n == 0){a = nullptr;top = capacity = 0;}else{a = (int*)malloc(sizeof(int) * n);if (a == nullptr){perror("realloc fail");exit(-1);}top = 0;capacity = n;}}void Init(){a = nullptr;top = capacity = 0;}void Push(int x){if (top == capacity){size_t newcapacity = capacity == 0 ? 4 : capacity * 2;int*tmp = (int*)realloc(a,sizeof(int) * newcapacity);if (tmp == nullptr){perror("realloc fail");exit(-1);}if (tmp == a){cout << capacity << "原地扩容" << endl;}else{cout << capacity << "异地扩容" << endl;}a = tmp;capacity = newcapacity;}a[top++] = x;}~Stack(){cout << "~Stack()" << endl;free(a);a = nullptr;top = capacity = 0;}int Top(){return a[top - 1];}void Pop(){assert(top > 0);--top;}void Destroy(){free(a);a = nullptr;top = capacity = 0;}bool Empty(){return top == 0;}
private:int* a;int top;int capacity;
};
int main()
{Date d1;Stack st1;Stack st2;//后定义的先析构return 0;
}

在这里插入图片描述
Stack中的成员变量a、capacity、top都是内置类型,对象st1生命周期结束要销毁的时候,a和capacity和top是在栈上不需要资源清理,最后由系统将其内存回收,而a指向的空间是在堆区上申请的,这块空间不会随着对象生命周期的结束而自动释放,所以会造成内存泄漏,因此在对象销毁前,要通过析构函数去释放成员变量a指向的空间,这就是析构函数的作用。

  • 🌏 关于编译器自动生成的析构函数,是否会完成一些事情呢?

下面的程序我们会看到,编译器生成的默认析构函数,对自定类型成员调用它的析构函数

#include<iostream>
using namespace std;
class Time
{
public:~Time(){cout << "~Time()" << endl;}
private:int _hour;int _minute;int _second;
};
class Date
{
private:// 基本类型(内置类型)int _year = 1970;int _month = 1;int _day = 1;// 自定义类型Time _t;
};
int main()
{Date d;return 0;
}

程序运行结束后输出:~Time()
在main方法中根本没有直接创建Time类的对象,为什么最后会调用Time类的析构函数?
因为:main方法中创建了Date对象d,而d中包含4个成员变量,其中_year, _month, _day三个是内置类型成员,销毁时不需要资源清理,最后系统直接将其内存回收即可;而_t是Time类对象,所以在d销毁时,要将其内部包含的Time类的_t对象销毁,所以要调用Time类的析构函数。
但是:main函数中不能直接调用Time类的析构函数,实际要释放的是Date类对象,所以编译器会调用Date类的析构函数,而Date没有显式提供,则编译器会给Date类生成一个默认的析构函数,目的是在其内部调用Time类的析构函数,即当Date对象销毁时,要保证其内部每个自定义对象都可以正确销毁main函数中并没有直接调用Time类析构函数,而是显式调用编译器为Date类生成的默认析构函数
注意:创建哪个类的对象则调用该类的析构函数,销毁那个类的对象则调用该类的析构函数;如果类中没有申请资源时(在堆上申请空间),析构函数可以不写,直接使用编译器生成的默认析构函数,比如Date(日期)类;有资源申请时,一定要写,否则会造成内存泄漏,比如Stack类


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

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

相关文章

20230904 QT客户端服务器搭建聊天室

Ser cpp#include "app.h" #include "ui_app.h"APP::APP(QWidget *parent):QWidget(parent),ui(new Ui::APP) {ui->setupUi(this);this->resize(550,400);ui->Line->setAlignment(Qt::AlignCenter);//标签文本对齐方式 居中ui->Line->se…

智能井盖传感器:高效守护城市道路安全

近年来&#xff0c;井盖出问题导致事故的报道时有发生&#xff0c;但却容易被公众所忽视。井盖作为城市基础设施的一部分&#xff0c;主要用于保护下方的供水管道、下水道以及电信线缆等。然而&#xff0c;由于长时间使用、缺乏维护、设计不合理等原因&#xff0c;井盖出现问题…

C语言——程序环境和预处理(再也不用担心会忘记预处理的知识)

了解程序环境和预处理 前言&#xff1a;一、程序环境二、编译链接2.1 翻译环境2.2 编译的几个阶段2.3 运行环境 三、预处理3.1 预定义符号3.2. #define的使用3.2.1 #define 定义标识符3.2.2 #define 定义宏3.2.3 #define 替换规则3.2.4 #和##的用途3.2.5 带副作用的宏参数3.2.6…

MySQL卸载干净再重新安装【Windows】

家人们&#xff0c;谁懂啊&#xff1f; 上学期学的数据库&#xff0c;由于上学期不知道为什么抽风&#xff0c;过得十分的迷&#xff0c;上课跟老师步骤安装好了Mysql&#xff0c;但后面在使用的过程中出现了问题&#xff0c;而且还出现了忘记密码这么蠢的操作&#xff0c;后半…

vue 子组件向父组件传递参数 子传父

子组件中写&#xff1a; this.$emit(RowCount,res.data.RowCount); 父组件中写&#xff1a; getMFGLRowCount(val){ //父组件中的方法: 接收子组件传过来的参数值赋值给父组件的变量 //this.totalCount val; alert("这…

无涯教程-JavaScript - IMREAL函数

描述 IMREAL函数以x yi或x yj文本格式返回复数的实系数。 语法 IMREAL (inumber)争论 Argument描述Required/OptionalInumberA complex number for which you want the real coefficient.Required Notes Excel中的复数仅存储为文本。 当将格式为" a bi"或&q…

【漏洞复现】一米OA存在任意文件读取漏洞

漏洞描述 一米OA协同办公系统,集成了OA办公自动化系统、手机客户端、专业报表工具,为全国千万企业用户提供全功能、性价比高的OA软件。 该OA系统的getfile.jsp文件存在任意文件上传漏洞,攻击者通过漏洞可以获取服务器的敏感信息。 免责声明 技术文章仅供参考,任何个人和…

分类预测 | Matlab特征分类预测全家桶(BP/SVM/ELM/RF/LSTM/BiLSTM/GRU/CNN)

分类预测 | Matlab特征分类预测全家桶&#xff08;BP/SVM/ELM/RF/LSTM/BiLSTM/GRU/CNN&#xff09; 目录 分类预测 | Matlab特征分类预测全家桶&#xff08;BP/SVM/ELM/RF/LSTM/BiLSTM/GRU/CNN&#xff09;预测效果基本介绍程序设计参考资料致谢 预测效果 基本介绍 分类预测 | …

sql:SQL优化知识点记录(十四)

&#xff08;1&#xff09;索引失效行锁变表锁 建立2个索引 索引是失效后&#xff0c;系统性能会变查&#xff0c;如果涉及到锁的话&#xff0c;行锁会变表锁 有一个问题&#xff0c;当session1用b字段做查询条件因为是varchar类型&#xff0c;需要加双引号&#xff0c;但是没…

Win10下使用vim9

作为一个经常与文字打交道的Writer&#xff0c;你在学会Vim的基本操作之后&#xff0c;就一定会爱上Vim的。 以下是Windows10_64位&#xff08;专业版&#xff09;环境中安装、使用Vim9的全过程&#xff0c;分享一下&#xff1a; 一、下载、安装Vim9 去Vim官网去下载最新的Vi…

BMS电池管理系统——BMS的功能模块及基本要素(二)

BMS电池管理系统 文章目录 BMS电池管理系统前言一、BMS电池管理系统各个功能模块的关系二、BMS的边界及基本要素 前言 前面了解了BMS以及他的功能模块&#xff0c;这些功能模块之间的关系是什么呢&#xff1f; 一、BMS电池管理系统各个功能模块的关系 下面我们分析一下这张图…

基于STM32设计的格力空调遥控器

一、格力空调协议介绍 格力空调的红外控制协议被称为格力红外通讯协议或者格力红外遥控协议。这个协议定义了一系列红外信号&#xff0c;可以用来控制格力空调的各种操作&#xff0c;例如开关、温度控制、模式选择、风速控制等等。 格力空调的红外控制协议是一种自定义协议&a…

nvm 在 Windows 上的使用

NVM&#xff08;Node Version Manager&#xff09;是一个用于管理和切换多个 Node.js 版本的工具。它允许你在同一台机器上同时安装和使用不同版本的 Node.js&#xff0c;而无需手动安装和卸载。 之前都是只安装一个版本的 node.js&#xff0c;该更新时更新&#xff0c;使得以前…

CocosCreator3.8研究笔记(十)CocosCreator 图像资源的理解

一、图像资源导入 Cocos Creator 可使用图像文件格式&#xff0c;支持 JPG、PNG、BMP、TGA、HDR、WEBBP、PSD、TIFF 等。 将图像资源直接拖拽到 资源管理器 即可将其导入 二、图像资源的类型 在 属性检查器 面板中便可根据需要设置图像资源的使用类型&#xff1a;raw 、 textu…

处理流程设计-系统设计-人机界面设计

处理流程设计-系统设计-人机界面设计 流程表示工具&#xff08;重点&#xff09; 流程表示工具&#xff08;重点&#xff09; 数据流图也是一种 IPO 图 NS图和PAD图

图像文件的操作MATLAB基础函数使用

简介 MATLAB中的图像处理工具箱体统了一套全方位的标准算法和图形工具&#xff0c;用于进行图像处理、分析、可视化和算法开发。这里仅仅对常用的基础函数做个使用介绍。 查询图像文件的信息 使用如下函数 imfinfo(filename,fmt) 函数imfinfo返回一个结构体的info&#xff…

【AI】机器学习——线性模型(逻辑斯蒂回归)

文章目录 3.3 逻辑斯蒂回归3.3.1 逻辑回归介绍对数几率函数sigmod函数几率 3.3.2 逻辑回归模型3.3.3 参数求解逻辑斯蒂回归策略 3.3.4 损失函数3.3.5 应用&#xff1a;语句情感判断3.3.6 多角度分析逻辑回归信息论角度数学角度与朴素贝叶斯对比 3.3.7 从二分类到多分类问题多次…

Netty(一)NIO-基础

Netty 分布式根基于网络编程&#xff0c;Netty恰是java网络编程的王者&#xff0c;致力于高性能编程。 前置 适用于网络开发&#xff0c;服务器开发。多线程&#xff0c;线程池&#xff0c;maven。 大纲 NIO编程&#xff08;Selector&#xff0c;ByteBuffer和Channel&…

【区块链】DeFi是什么?大白话科普文

对于一些没有玩过区块链、或者说没有真金白银的体验过这个虚拟世界的小伙伴来说,这篇文章可以帮你了解 DeFi。致力于帮你在这个线上走出这一步。 当然这不是理财建议。 文章目录 前言什么是去中心化金融?有哪些 DeFi 项目DeFi由哪几部分构成?热门DeFi项目有哪些?前言 Def…

Gradle下载库速度过慢解决办法

最近搞了个Gradle的项目&#xff0c;项目下载依赖库太慢了&#xff0c;于是… Gradle下载库速度过慢的问题可能由多种原因导致&#xff0c;以下是一些可能的解决方案&#xff1a; 1、使用国内镜像站点&#xff1a; 你可以改变Gradle的配置&#xff0c;使用国内的镜像站点来下…