【C++修行之道】类和对象(二)类的6个默认成员函数、构造函数、析构函数
目录
一、类的6个默认成员函数
二、构造函数
2.1 概念
2.2 特性
2.2.5 自动生成默认构造函数
不进行显示定义的隐患:
2.2.6 自动生成的构造函数意义何在?
两个栈实现一个队列
2.2.7 无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。
2.4 一般情况,建议每个类,都可以写一个全缺省的构造(好用)
三、析构函数
3.1 概念
3.2 特性
3.3 C++实现括号匹配和C语言的不同
一、类的6个默认成员函数
如果一个类中什么成员都没有,简称为空类。
空类中真的什么都没有吗?并不是,任何类在什么都不写时,编译器会自动生成以下6个默认成员函数。
默认成员函数:用户没有显式实现,编译器会自动生成(半自动化)的成员函数称为默认成员函数。
class Date {};

二、构造函数
2.1 概念
对于以下Date类:
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();Date d2;d2.Print();return 0;
}
对于Date类,可以通过 Init 公有方法给对象设置日期,但如果每次创建对象时都调用该方法设置信息,未免有点麻烦,那能否在对象创建时,就将信息设置进去呢?
构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,以保证每个数据成员都有 一个合适的初始值,并且在对象整个生命周期内只调用一次。
2.2 特性
构造函数是特殊的成员函数,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任务并不是开空间创建对象,而是初始化对象。
其特征如下:
- 函数名与类名相同。
- 无返回值。
- 对象实例化时编译器自动调用对应的构造函数。
- 构造函数可以重载。


class Date
{
public:// 1.无参构造函数Date(){}// 2.带参构造函数Date(int year, int month, int day){_year = year;_month = month;_day = day;}
private:int _year;int _month;int _day;
};void TestDate()
{Date d1; // 调用无参构造函数Date d2(2015, 1, 1); // 调用带参的构造函数// 注意:如果通过无参构造函数创建对象时,对象后面不用跟括号,否则就成了函数声明// 以下代码的函数:声明了d3函数,该函数无参,返回一个日期类型的对象// warning C4930: “Date d3(void)”: 未调用原型函数(是否是有意用变量定义的?)Date d3();
}
在C++中,当编译器看到一个像Date d3();这样的声明时,它会根据语法规则将其解析为一个函数声明,而不是一个对象定义。这是因为C++的语法允许这样的歧义,而且函数声明的优先级高于对象定义。因此,即使你本意是想创建一个对象,编译器也会将其视为一个函数声明。为了避免这种歧义,最好不要在对象定义时使用空括号。
2.2.5 自动生成默认构造函数
如果类中没有显式定义构造函数,则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类中构造函数屏蔽后,代码可以通过编译,因为编译器生成了一个无参的默认构造函数// 将Date类中构造函数放开,代码编译失败,因为一旦显式定义任何构造函数,编译器将不再 生成// 无参构造函数,放开后报错:error C2512: “Date”: 没有合适的默认构造函数可用Date d1;return 0;
}
在这个Date类的定义中,并没有显式定义任何构造函数。编译器会自动生成一个默认的无参构造函数。这个自动生成的构造函数不会进行任何实质性的初始化操作。
不进行显示定义的隐患:
class A
{
public:A(){_a = 0;}
private:int _a;
};class Time {
public:private:int _hour;int _minute;int _second;A _aa;
};class Date
{
public:void Print() {cout << _year << _month << _day << endl;}
private:// 默认生成构造函数// 内置类型没有规定要处理(可处理,可不处理,看编译器)int _year;int _month;int _day;// 自定义类型调用默认构造函数Time _t;
};int main()
{//Date d1(2024, 4, 9);//d1.Print();Date d2;d2.Print();return 0;
}


2.2.6 自动生成的构造函数意义何在?
关于编译器生成的默认成员函数,很多童鞋会有疑惑:不实现构造函数的情况下,编译器会生成默认的构造函数。但是看起来默认构造函数又没什么用?d对象调用了编译器生成的默认构造数,但是d对象_year/_month/_day,依旧是随机值。也就说在这里编译器生成的默认构造函数并没有什么用?
解答:C++把类型分成内置类型(基本类型)和自定义类型。内置类型就是语言提供的数据类型,如:int/char...,自定义类型就是我们使用class/struct/union等自己定义的类型,看看下面的程序,就会发现编译器生成默认的构造函数会对自定类型成员_t调用的它的默认成员函数。
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 = 1970;int _month = 1;int _day = 1;// 自定义类型Time _t;
};
int main()
{Date d;return 0;
}
注意:C++11 中针对内置类型成员不初始化的缺陷,又打了补丁,即:内置类型成员变量在类中声明时可以给默认值。
在实际应用中,通常建议在类的构造函数中初始化这些成员,以确保对象的状态是确定的。

两个栈实现一个队列
typedef int DataType;
class Stack
{
public:Stack(size_t capacity = 10){_array = (DataType*)malloc(capacity * sizeof(DataType));if (nullptr == _array){perror("malloc申请空间失败");return;}_size = 0;_capacity = capacity;}void Push(const DataType& data){// CheckCapacity();_array[_size] = data;_size++;}~Stack(){if (_array){free(_array);_array = nullptr;_capacity = 0;_size = 0;}}
private:DataType* _array;size_t _size;size_t _capacity;
};
// 自动生成的构造函数意义何在?
class MyQueue
{
private:Stack _pushst;Stack _popst;
};int main()
{MyQueue q;return 0;
}

自动生成的构造函数意义何在?
- 确保成员变量的正确初始化:自动生成的构造函数会调用成员变量的默认构造函数,确保 MyQueue 中的两个 Stack 对象在 MyQueue 对象创建时被正确初始化。
- 简化代码:由于 MyQueue 类在这个例子中没有特殊的初始化需求,因此不需要手动编写构造函数。
- 如果类中没有需要特别处理的初始化逻辑,那么自动生成的构造函数可以简化代码,避免不必要的冗余。这样,开发者可以专注于类的其他功能和逻辑,而不必担心基本的初始化问题。
2.2.7 无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。
注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认构造函数。
class Date
{
public:Date(){_year = 1900;_month = 1;_day = 1;}Date(int year = 1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;}
private:int _year;int _month;int _day;
};
// 以下测试函数能通过编译吗?
void Test()
{Date d1;
}

2.4 一般情况,建议每个类,都可以写一个全缺省的构造(好用)
class Date
{
public:// 他们俩构成函数重载,但是无参调用时会存在歧义/*Date(){_year = 1;_month = 1;_day = 1;}*/// 一般情况,建议每个类,都可以写一个全缺省的构造(好用)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 d1;d1.Print();Date d2(2024, 4, 2);// 对象(参数列表)d2.Print();Date d3(2024);d3.Print();Date d4(2024, 4);d4.Print();return 0;
}

三、析构函数
3.1 概念
通过前面构造函数的学习,我们知道一个对象是怎么来的,那一个对象又是怎么没呢的?析构函数:与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作。

3.2 特性

析构函数是特殊的成员函数,其特征如下:
- 析构函数名是在类名前加上字符 ~。
- 无参数无返回值类型。
- 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构函数不能重载
- 对象生命周期结束时,C++编译系统系统自动调用析构函数。
typedef int DataType;class Stack
{
public:Stack(size_t capacity = 3){cout << "Stack(size_t capacity = 3)" << endl;_array = (DataType*)malloc(sizeof(DataType) * capacity);if (NULL == _array){perror("malloc申请空间失败!!!");return;}_capacity = capacity;_size = 0;}void Push(DataType data){// CheckCapacity();_array[_size] = data;_size++;}// 其他方法...~Stack(){cout << "~Stack()" << endl;if (_array){free(_array);_array = NULL;_capacity = 0;_size = 0;}}
private:DataType* _array;int _capacity;int _size;
};int main()
{Stack st;return 0;
}

5. 关于编译器自动生成的析构函数,是否会完成一些事情呢?下面的程序我们会看到,编译器生成的默认析构函数,对自定类型成员调用它的析构函数。
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是Tim类对象,所以在d销毁时,要将其内部包含的Time类的_t对象销毁,所以要调用Time类的析构函数。但是:main函数中不能直接调用Time类的析构函数,实际要释放的是Date类对象,所以编译器会调用Date类的析构函数,而Date没有显式提供,则编译器会给Date类生成一个默认的析构函数。
目的是:在其内部调用Time类的析构函数,即当Date对象销毁时,要保证其内部每个自定义对象都可以正确销毁,main函数中并没有直接调用Time类析构函数,而是显式调用编译器为Date类生成的默认析构函数
注意:创建哪个类的对象则调用该类的析构函数,销毁那个类的对象则调用该类的析构函数
6. 如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数,比如Date类;有资源申请时,一定要写,否则会造成资源泄漏,比如Stack类。
3.3 C++实现括号匹配和C语言的不同
可以明显的看出,C++对应C语言来说简化了不少,对C语言进行了一定的优化。

今天就先到这了!!!

看到这里了还不给博主扣个:
⛳️ 点赞☀️收藏 ⭐️ 关注!
你们的点赞就是博主更新最大的动力!
有问题可以评论或者私信呢秒回哦。
相关文章:
【C++修行之道】类和对象(二)类的6个默认成员函数、构造函数、析构函数
目录 一、类的6个默认成员函数 二、构造函数 2.1 概念 2.2 特性 2.2.5 自动生成默认构造函数 不进行显示定义的隐患: 2.2.6 自动生成的构造函数意义何在? 两个栈实现一个队列 2.2.7 无参的构造函数和全缺省的构造函数都称为默认构造函数&#x…...
【LeetCode热题100总结】239. 滑动窗口最大值
题目描述 给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。 返回 滑动窗口中的最大值 。 示例 1: 输入:nums [1,3,-1,-3,5,3,6,7]…...
【YOLOv9改进[Conv]】使用YOLOv10的空间通道解耦下采样SCDown模块替换部分CONv的实践 + 含全部代码和详细修改内容
本文将使用YOLOv10的空间通道解耦下采样SCDown模块替换部分CONv的实践 ,文中含全部代码和详细修改内容。 目录 一 YOLOv10 1 空间通道解耦下采样 2 可视化...
简单小游戏制作
控制台基础设置 //隐藏光标 Console.CursorVisible false; //通过两个变量来存储舞台的大小 int w 50; int h 30; //设置舞台(控制台)的大小 Console.SetWindowSize(w, h); Console.SetBufferSize(w, h);多个场景 int nowSceneID 1; while (true) …...
Delphi
Delphi,是美国 Borland(宝兰)公司於 1995 年开发在 Windows 平台下的快速应用程式开发工具 (Rapid Application Development,简称 RAD),它的前身是在 DOS 下的产品 Borland Turbo Pascal。(非开源软件&…...
Linux的shell脚本中的比大小
如果要将 -le 换成相反的(即“大于”),你应该使用 -gt(greater than)。 所以,-le 的相反比较是 -gt。 但如果你想要一个包含“大于”和“不等于”的比较(即“大于”),那…...
每日复盘-20240603
20240603 六日涨幅最大: ------1--------300637--------- 扬帆新材 五日涨幅最大: ------1--------300637--------- 扬帆新材 四日涨幅最大: ------1--------301306--------- 西测测试 三日涨幅最大: ------1--------301306--------- 西测测试 二日涨幅最大: ------1--------30…...
adb server version (22000) doesn‘t match this client (41); killing...
参考链接: adb server version (31) doesn’t match this client (41); killing… 解决此问题 电脑安装了360手机助手占用了adb的端口引起的。因为套接字的唯一性(一个套接字只能由 协议/网络地址/端口号 唯一确定 ),一个电脑只能有一个程序…...
如何使用 Connector API 将数据提取到 Elasticsearch Serverless 中
作者:来自 Elastic Jedr Blaszyk Elasticsearch 支持一系列摄取方法。 其中之一是 Elastic Connectors,它将 SQL 数据库或 SharePoint Online 等外部数据源与 Elasticsearch 索引同步。 连接器对于在现有数据之上构建强大的搜索体验特别有用。 例如&…...
体育赛事直播系统开发源码搭建
随着体育产业的蓬勃发展,体育赛事直播已成为广大观众获取赛事信息的重要途径。为了满足观众日益增长的需求,开发一套专业的体育赛事直播系统成为当务之急。本文将围绕体育赛事直播系统开发源码搭建进行深入探讨,从技术选型、系统架构、安全防…...
使用Jmeter进行性能测试
学习视频 B站UP主:白月黑羽编程 目录 Jmeter的下载 Jmeter界面 Jmeter操作 线程组与HTTP请求 测试一个请求 解决响应数据中 中文乱码的问题 HTTP请求默认值 录制网站流量 添加录制控制器 添加HTTP代理服务器 在浏览器配置代理 进行录制 模拟间隔时间 …...
AI技术的发展,会让你工作轻松吗
这两年,人工智能技术迅猛发展,AI产品层出不穷,尤其大语言模型(LLM)的爆火,许多人担心自己的工作会被AI取代。然而,如果AI技术发展到极致,再加上其他科学技术的高度发展,我…...
Spring-DI入门案例
黑马程序员SSM框架教程 文章目录 一、DI入门案例思路分析二、实现步骤2.1 删除service中使用new形式创建的Dao对象2.2 提供以来对象对应的setter方法2.3 配置service与到之间的关系 一、DI入门案例思路分析 基于IoC管理bean(上个案例已经实现)service中…...
ubuntu18.04 报错:fatal error: execution
一、问题描述 在ubuntu18.04上编译Faster-lio 报错: fatal error: execution: 没有那个文件或目录#include <execution> 二、解决方法 需要将g编译器更新到9.0 sudo add-apt-repository ppa:ubuntu-toolchain-r/test sudo apt update sudo apt install g…...
开源大模型与大模型api的使用优缺点
开源大模型和大模型 API 都是人工智能领域中的重要概念,它们各自有一些优缺点。 开源大模型: 优点: 免费使用: 大多数开源大模型是免费提供的,任何人都可以访问和使用这些模型,降低了使用门槛。可定制性…...
小红书前端2轮面试期望22K,全程问低代码设计
一面(通过) 1、好,那我们开始把,先简单介绍一下自己的一个经历,以及自己有亮点的项目?balabala 2、你可以这样介绍:在这里边主要负责哪几个项目,哪些项目是比较有亮点的࿰…...
HIK录像机GB28181对接相机不在线问题随笔
一、问题现象 【设备信息】型号:DS-8664N-I16-V3 V4.63.000 build 230412 【问题现象】HIK录像机使用GB28181对接异常相机无法正常上线,对接HIK相机可以正常上线。 【现场拓扑】现场拓扑如下 NVR侧使用固定公网IP地址。IPC侧使用家用宽带的方式&…...
stm32-DMA转运数据
在配置前要记得先定义一下DMA转运的源端数组和目标数组两个数组哦。 接下来我们就开始准备配置吧 配置 初始化 1.RCC开启时钟(开启DMA的时钟) void RCC_AHBPeriphClockCmd(uint32_t RCC_AHBPeriph, FunctionalState NewState) 作用:开启时…...
Java编程常见问题汇总一
系列文章目录 文章目录 系列文章目录前言一、字符串连接误用二、错误的使用StringBuffer三、测试字符串相等性四、数字转换成字符串五、利用不可变对象(Immutable) 前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分…...
用Unityhub安装unity2018.3.0和vuforia
打开下载网址 https://unity.cn/releases/full/2018 选择2018.3.x 找到2018.3.0后,点击从UnityHub下载 然后unityhub会弹出安装界面 只勾选这两个,其余的全部取消勾选,默认勾选上的也取消掉,然后点击安装...
【Oracle APEX开发小技巧12】
有如下需求: 有一个问题反馈页面,要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据,方便管理员及时处理反馈。 我的方法:直接将逻辑写在SQL中,这样可以直接在页面展示 完整代码: SELECTSF.FE…...
用docker来安装部署freeswitch记录
今天刚才测试一个callcenter的项目,所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...
GC1808高性能24位立体声音频ADC芯片解析
1. 芯片概述 GC1808是一款24位立体声音频模数转换器(ADC),支持8kHz~96kHz采样率,集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器,适用于高保真音频采集场景。 2. 核心特性 高精度:24位分辨率,…...
Redis的发布订阅模式与专业的 MQ(如 Kafka, RabbitMQ)相比,优缺点是什么?适用于哪些场景?
Redis 的发布订阅(Pub/Sub)模式与专业的 MQ(Message Queue)如 Kafka、RabbitMQ 进行比较,核心的权衡点在于:简单与速度 vs. 可靠与功能。 下面我们详细展开对比。 Redis Pub/Sub 的核心特点 它是一个发后…...
音视频——I2S 协议详解
I2S 协议详解 I2S (Inter-IC Sound) 协议是一种串行总线协议,专门用于在数字音频设备之间传输数字音频数据。它由飞利浦(Philips)公司开发,以其简单、高效和广泛的兼容性而闻名。 1. 信号线 I2S 协议通常使用三根或四根信号线&a…...
处理vxe-table 表尾数据是单独一个接口,表格tableData数据更新后,需要点击两下,表尾才是正确的
修改bug思路: 分别把 tabledata 和 表尾相关数据 console.log() 发现 更新数据先后顺序不对 settimeout延迟查询表格接口 ——测试可行 升级↑:async await 等接口返回后再开始下一个接口查询 ________________________________________________________…...
【堆垛策略】设计方法
堆垛策略的设计是积木堆叠系统的核心,直接影响堆叠的稳定性、效率和容错能力。以下是分层次的堆垛策略设计方法,涵盖基础规则、优化算法和容错机制: 1. 基础堆垛规则 (1) 物理稳定性优先 重心原则: 大尺寸/重量积木在下…...
6个月Python学习计划 Day 16 - 面向对象编程(OOP)基础
第三周 Day 3 🎯 今日目标 理解类(class)和对象(object)的关系学会定义类的属性、方法和构造函数(init)掌握对象的创建与使用初识封装、继承和多态的基本概念(预告) &a…...
【java】【服务器】线程上下文丢失 是指什么
目录 ■前言 ■正文开始 线程上下文的核心组成部分 为什么会出现上下文丢失? 直观示例说明 为什么上下文如此重要? 解决上下文丢失的关键 总结 ■如果我想在servlet中使用线程,代码应该如何实现 推荐方案:使用 ManagedE…...
背包问题双雄:01 背包与完全背包详解(Java 实现)
一、背包问题概述 背包问题是动态规划领域的经典问题,其核心在于如何在有限容量的背包中选择物品,使得总价值最大化。根据物品选择规则的不同,主要分为两类: 01 背包:每件物品最多选 1 次(选或不选&#…...
