C++初阶——类和对象(三) 构造函数、析构函数

news2025/3/17 20:43:49

C++初阶——类和对象(三)

上期内容,我们围绕类对象模型的大小计算成员存储方式this指针,以及C++实现栈和C语言的比较,进一步认识了C++的封装特性。本期内容,我们开始介绍类的默认成员函数,这是类和对象板块中最为重要,也是相对复杂的内容,让我们一起探索!

引言

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

  • 初始化和清理:构造函数析构函数
  • 拷贝复制:拷贝构造函数赋值重载函数
  • 取地址重载:普通对象取地址重载const对象取地址重载函数

一、构造函数

1.背景引入

示例1
示例2

我们再来回顾一下,_year_month_day是三个私有的成员变量,InitPrint是外界可以直接访问的成员函数,在main函数中,我们先要通过类来示例化出一个对象,比如这里的Date d1; Date d2; Date d3;等等,然后调用类里面的成员函数。这里基于同一个类创建了很多对象,然而在成员函数中似乎并没有传递代表各个对象的形参,那么编译器是怎么区分的呢?很简单,由于this指针的存在。我们以d1.Init(2025,3,10);为例,this指针指向当前的对象——d1,然后在调用函数Init时,隐式传递了这个指针,然后通过这个指针访问d1中的成员变量_year、_month、_day,将它们分别赋值为2025、3、10,如图所示:
示例3
当运行到下一个对象d2时,this指针指向的就是d2了。

进一步探究

如果我们仔细观察,不难发现这里分为了两步走:第一步,先通过类实例化一个对象,也就是Date d1;,然后再用具体的数值将其初始化d1.Init(2025,3,10);,其实是有些麻烦的,我们能不能在实例化对象的同时,就将信息设置进去呢?——答案是肯定的,C++本身就是对C语言的优化,构造函数应运而生!

2.什么是构造函数?

构造函数是一个特殊的成员函数,名字与类名相同。创建类对象时由编译器自动调用,以保证每个数据成员都有一个合适的初始值,并且在对象整个生命周期内只调用一次
示例4
示例5
在这里,我们已经将原来的初始化函数改造成了一个构造函数,构造函数的函数名与类名相同,没有返回值(这里的没有返回值不是说返回一个void,写成void Date(int year, int month, int day),而是什么都不要写,直接就是Date(int year, int month, int day)),至于为什么,这是语法规定,这是一个特殊的函数,有特殊的待遇,只有这样写,编译器才知道这是构造函数。对于构造函数的调用,也是很有趣的,我们在实例化对象的同时,后面跟上了一个括号,里面就是想要在对象中填入的信息Date d1(2025, 3, 10);,这样一来,我们不仅实例化了一个对象,还填入了我们想要的信息,一步解决,非常方便。之前的那种两步走,是先根据图纸建房子,然后里面的家具还要自己来置办;现在是把装修也一并让别人做好了,自己直接拎包入住,确实方便的多。

3.构造函数的特性

构造函数是特殊的成员函数,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任务并不是开空间创建对象,而是初始化对象。也很好理解,之前是两步走,现在是一步走,至于减少的那一步——填入想要的信息,就是构造函数的功劳。

(1)构造函数的重载

构造函数是可以重载的,根据传参的不同,调用对应的构造函数,如图所示:
示例7
这里就写了两个构造函数,其中一个是无参数的,这些都可以理解,但是,我们注意到,为什么在调用的时候写的是Date d1;而不是Date d1();呢?我们先来看一下结果:
示例8
这里什么都没有输出,也就是没有调用那个构造函数,这是为什么呢?仔细观察我们发现,Date d1();像是函数的声明,Date返回值的类型d1函数名,()表示这个函数不需要传参。基于这种情况,编译器不知道这里到底是在类实例化对象还是在声明一个名为d1的函数。因此,我们对于无参的构造函数,在实例化对象时就不需要加()
当然,还有一点,一开始的构造函数示例中,是提供了缺省参数的,而且是全缺省,这里做出了改变。如果依然是全缺省会发生什么呢?如图所示:
示例9
示例10
还是一个调用不明确的问题:不传参,不仅无参的函数可以调用,全缺省的也可以调用,因为它可以自动帮你把数据补上。在C++入门中已经讲过这一点,这里就当是复习一下吧。

(2)默认构造函数
默认构造函数的分类

默认构造函数是无需参数即可调用的构造函数,我们既可以显式声明,也可以让编译器隐式生成。无参构造函数全缺省构造函数我们没写编译器默认生成的构造函数,都可以认为是默认构造函数,再简单一点,不需要传参就可以调用的构造函数都是默认构造函数:

  • 无参构造函数
    示例11
  • 全缺省构造函数
    示例12
  • 编译器默认生成的构造函数
    示例13
编译器自动生成的构造函数

如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成。注意,这里是无参的,也就是说调用的时候直接写Date d1;即可,如图所示:
示例14
示例15
这里的默认构造函数似乎没什么用,对于这些变量的值,好像也没有进行处理,依旧是随机值,那么默认构造函数还有它存在的价值吗?
其实是这样的:C++把类型分成内置类型(基本类型)和自定义类型。内置类型就是语言提供的数据类型,如:int、char、double……,自定义类型就是需要自己定义的,如struct、class……
编译器生成的默认构造函数对内置类型不作处理对自定义类型则是调用它的默认构造。因此,一般情况下,有内置类型成员,就需要自己写构造函数,不能用编译器自己生成的;全部都是自定义类型成员,可以考虑让编译器自己生成。
我们来举个自定义类型的例子:
示例16
在Date类的成员变量中,不仅有内置类型,还有自定义类型变量Time _t;那么在实例化对象时,内置类型不作处理自定义类型调用它的默认构造,而Time类的默认构造我们已经自己定义了,因此会打印出对应的结果,我们也可以调试看一看:
示例17
由于Date类中没有自己写构造函数,内置类型都是随机值,而自定义类型Time _t就调用了它的默认构造,而Time的默认构造是我们自己实现的,对内置类型进行了初始化。

  • C++11 中针对内置类型成员不初始化的缺陷,又打了补丁,即:内置类型成员变量在类中声明时可以给默认值:
    示例18
    这里没有自己写构造函数,默认值给在成员变量的声明里。

二、析构函数

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

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

1.自己定义的析构函数

我们以一个为例,因为栈中涉及到动态内存开辟,不使用了需要及时销毁开辟的内存空间,否则会造成内存泄漏,有了析构函数,我们只需要在类中定义一下,在类外它就会自动调用了,代码如下:

typedef int DataType;
class Stack
{
public:
	Stack(size_t capacity = 3)
	{
		_array = (DataType*)malloc(sizeof(DataType) * capacity);
		if (NULL == _array)
		{
			perror("malloc fail");
			return;
		}
		_capacity = capacity;
		_size = 0;
	}
	void Push(DataType data)
	{
		// CheckCapacity();
		_array[_size] = data;
		_size++;
	}
	// 其他方法...
	~Stack()
	{
		if (_array)
		{
			free(_array);
			_array = NULL;
			_capacity = 0;
			_size = 0;
		
		}
		cout << "~Stack" << endl;
	}
private:
	DataType* _array;
	int _capacity;
	int _size;
};

int main()
{
	Stack st1;

	return 0;
}

运行结果如图:
示例19

2.编译器自动生成的析构函数

关于编译器自动生成的析构函数,是否会完成一些事情呢?下面的程序我们会看到,编译器生成的默认析构函数,对自定类型成员调用它的析构函数。
示例20
如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数,比如Date类;有资源申请时,一定要写,否则会造成资源泄漏,比如Stack类。

本期总结+下期预告

本期内容非常重要,详细介绍了类的构造函数和析构函数,下期将继续讲解拷贝构造函数等相关内容!

感谢大家的关注,我们下期再见!
在这里插入图片描述

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

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

相关文章

【Function】使用托管身份调用Function App触发器,以增强安全性

推荐超级课程: 本地离线DeepSeek AI方案部署实战教程【完全版】Docker快速入门到精通Kubernetes入门到大师通关课AWS云服务快速入门实战目录 1. 背景介绍2. 设置3. 使用Web应用调用Function App触发器(Node.js示例)4. 执行结果此方法允许您使用托管身份(Managed Identity)调…

x012-MSP430F249智能步进电动百叶窗_proteus_光敏电阻_步进电机_仿真

https://www.dong-blog.fun/post/1997 46 、智能步进电动百叶窗 基本要求&#xff1a; 用一台步进电机控制百叶窗叶片的旋转&#xff08;正转/反转&#xff09; 用 LED 数码管显示旋转角度 设置按键&#xff1a; 手动/自动切换、手动正转和手动反转&#xff0c;停止/启动键 用一…

牛客周赛85 题解 Java ABCDEFG

A小紫的均势博弈 判断输入的 n 是奇数还是偶数 import java.io.*; import java.math.*; import java.util.*;public class Main {static IoScanner sc new IoScanner();static final int mod(int) (1e97);static void solve() throws IOException {int nsc.nextInt();if(n%2…

# RAG 框架 # 一文入门 全链路RAG系统构建与优化 —— 架构、策略与实践

本文全面阐述了RAG系统从数据收集、数据清洗&#xff08;包括领域专有名词处理&#xff09;、智能数据分块与QA对生成&#xff0c;到向量化、向量数据库选择与配置&#xff0c;再到检索方式及重排序&#xff0c;直至整合输出、监控反馈和安全保障的全流程。通过这一完整方案&am…

【Golang】第二弹-----变量、基本数据类型、标识符

笔上得来终觉浅,绝知此事要躬行 &#x1f525; 个人主页&#xff1a;星云爱编程 &#x1f525; 所属专栏&#xff1a;Golang &#x1f337;追光的人&#xff0c;终会万丈光芒 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 目录 一、变量 1.1基本介绍…

linux系统CentOS 7版本搭建NFS共享存储

一、什么是NFS共享存储方式 NFS共享存储方式 是一种分布式文件系统协议&#xff0c;允许客户端通过网络访问远程服务器上的文件&#xff0c;就像访问本地文件一样。 二、 NFS的基本概念 &#xff08;1&#xff09;服务器端&#xff1a;提供共享存储的机器&#xff0c;负责导…

Matlab 基于SVPWM的VF三电平逆变器异步电机速度控制

1、内容简介 略 Matlab 167-基于SVPWM的VF三电平逆变器异步电机速度控制 可以交流、咨询、答疑 2、内容说明 略 3、仿真分析 略 4、参考论文 略

(一)微服务初见之 Spring Cloud 介绍

微服务架构简介 从单体应用架构发展到SOA架构&#xff0c;再到微服务架构&#xff0c;应用架构经历了多年的不断演进。微服务架构不是凭空产生的&#xff0c;而是技术发展的必然结果&#xff0c;分布式云平台的应用环境使得微服务代替单体应用成为互联网大型系统的架构选择。目…

架构思维:软件建模与架构设计的关键要点

文章目录 1. 软件建模的核心概念2. 七种常用UML图及其应用场景类图时序图组件图部署图用例图状态图活动图 3. 软件设计文档的三阶段结构4. 架构设计的关键实践1. 用例图&#xff1a;核心功能模块2. 部署图&#xff1a;架构演进阶段3. 技术挑战与解决方案4. 关键架构图示例5. 架…

【RNN神经网络】序列模型与RNN神经网络

前言 清库存。正式切入大模型后&#xff0c;打算把基础知识都梳理一遍&#xff0c;然后写了两篇就发现写不动了&#xff0c;后面就捡重要的记录。RNN知识仅此一篇记录&#xff0c;扫盲记录。 【自然语言处理】 &#xff08;Natural Language Processing&#xff0c;NLP&#xf…

Python文件管理

目录 一、文本文件读写 1、相关函数 2、读写文件 3、使用readline读取一行 4、读写文件的异常处理 5、添加内容 二、文本文件的编码 1、常见的编码 2、Python程序的编码 3、指定编码 三、文件的路径 1、相对路径 2、绝对路径 3、路径的改变 四、文件夹操作 五、…

vue3 前端路由权限控制与字典数据缓存实践(附Demo)

目录 前言1. 基本知识2. Demo3. 实战 前言 &#x1f91f; 找工作&#xff0c;来万码优才&#xff1a;&#x1f449; #小程序://万码优才/r6rqmzDaXpYkJZF 从实战中出发&#xff1a; 1. 基本知识 Vue3 和 Java 通信时如何进行字典数据管理 需要了解字典数据的结构。通常&#x…

基于javaweb的SpringBoot精美物流管理系统设计与实现(源码+文档+部署讲解)

技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;免费功能设计、开题报告、任务书、中期检查PPT、系统功能实现、代码编写、论文编写和辅导、论…

DeepSeek进阶应用(二):结合Kimi制作PPT(双AI协作教程)

&#x1f31f;引言&#xff1a; DeepSeek作为国产AI大模型&#xff0c;以强大的逻辑推理和结构化内容生成能力著称&#xff0c;擅长根据用户需求生成PPT大纲或Markdown文本&#xff1b;Kimi的PPT助手则能解析结构化内容并套用模板快速生成美观的PPT&#xff0c;两者结合实现“内…

SpringBoot——Maven篇

Spring Boot 是一个用于快速开发基于 Spring 框架的应用程序的工具。它具有许多特性&#xff0c;其中一些重要的特性包括&#xff1a; 1. 自动配置&#xff1a;Spring Boot 提供了自动配置的机制&#xff0c;可以根据应用程序的依赖和环境自动配置应用程序的各种组件&#xff…

卷积神经网络(知识点)

一、为了使特征图变小&#xff1a; 由两种方法&#xff1a;1.增大步长&#xff1a;卷积的时候不是一次一步&#xff0c;而是一次多步&#xff0c;类似一张图片&#xff0c;在原来的像素基础上&#xff0c;每隔一个取一个像素点。 其中S就是步长 注意&#xff1a;扩大步长不经…

Vision Transformer (ViT):将Transformer带入计算机视觉的革命性尝试(代码实现)

Vision Transformer (ViT)&#xff1a;将Transformer带入计算机视觉的革命性尝试 作为一名深度学习研究者&#xff0c;如果你对自然语言处理&#xff08;NLP&#xff09;领域的Transformer架构了如指掌&#xff0c;那么你一定不会对它在序列建模中的强大能力感到陌生。然而&am…

特殊 IP 地址

文章目录 特殊IP地址概述受限广播地址&#xff08;Limited Broadcast Address&#xff09;直接广播地址&#xff08;Directed Broadcast Address&#xff09;多播地址&#xff08;Multicast Address&#xff09;环回地址&#xff08;Loopback Address&#xff09;本网络本主机&…

数学——A. K-divisible Sum + D. Exam in MAC

A. K-divisible Sum 题目&#xff1a; 思路&#xff1a; 以下 “[xxx]” 符号均代表向上取整 我们假设总和是sum&#xff0c;那么就有sum k * cnt 要想最大值最小&#xff0c;肯定是要让sum尽可能小&#xff0c;这样每个元素都能变小 最小情况是 sum 恰好等于 n 时&#…

【DeepSeek应用】本地部署deepseek模型后,如何在vscode中调用该模型进行代码撰写,检视和优化?

若已成功在本地部署了 DeepSeek 模型(例如通过 vscode-llm、ollama 或私有 API 服务),在 VS Code 中调用本地模型进行代码撰写、检视和优化的完整流程如下: 1. 准备工作:确认本地模型服务状态 模型服务类型: 若使用 HTTP API 服务(如 FastAPI/Flask 封装),假设服务地址…