【C++初阶】构造函数和析构函数

在这里插入图片描述

文章目录

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

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

📖默认成员函数
用户没有显式实现,编译器会自动生成的成员函数,称为默认成员函数。
在这里插入图片描述

  • 构造函数:完成对象的初始化工作。
  • 析构函数:完成对象空间的清理工作。
  • 拷贝构造:使用同类对象初始化创建对象。
  • 赋值重载:把一个对象赋值给另外一个对象(该对象已存在)。
  • 取地址重载:获取对象的地址,这两个很少自己实现。

注意:构造和析构函数,不是创建对象和销毁对象。对象的创建和销毁都是编译器做的工作。

二、构造函数

📖为什么要有构造函数?
为了避免每次创建对象后,都要去调用专门的成员函数设置对象的信息,这样很麻烦,并且容易遗忘,那就想着能否在创建对象的同时,就将信息设置进去。因此,就有了构造函数。以日期类为例:

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.Init(2022, 7, 5);//调用初始化函数
	d1.Print();
	Date d2;
	d2.Init(2022, 7, 6);
	d2.Print();
	return 0;
}

如上面的代码,每次创建一个日期类对象后,都要手动的去调用Init函数,完成对象的初始化,整个过程繁琐,而且容易遗忘,为此,提出了构造函数的概念。

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

📖构造函数的特性

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

🎊示例:

 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); // 调用带参的构造函数
      Date d3();
 }

注意:如果通过无参构造函数创建对象时,对象后面不用跟括号,否则就成了函数声明。即Date d3();是声明了一个d3函数,该函数无参,返回一个日期类对象,并不是创建了一个日期类对象d3

构造函数在语法上可以是私有的,但是在创建对象的时候就调不动了。在单例模式中,会把构造函数搞成私有,具体的我们以后再说。

📖编译器生成的构造函数
如果类中没有显式定义构造函数,则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类中的构造函数放开后,代码编译失败,因为一旦显式定义了任何构造函数,编译器将不再生成默认构造函数。而此时Date中的构造函数需要三个参数,Date d1;会去调用无参的构造函数,但是当前类中没有无参的构造函数,所以编译会报错。

📖编译器生成的构造函数干了什么?
C++中把类型分为内置类型自定义类型。内置类型就是语言提供的数据类型,如:intchar……自定义类型就是我们使用classstructunion等自己定义的类型。(所有类型的指针都属于内置类型)。

编译器生成的默认构造函数,对内置类型不做处理,对自定义类型,会去调用它的默认构造函数

🎊示例:

//先定义一个时间类
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;
}

在这里插入图片描述

上面代码,先定义了一个时间类Time,它的成员变量都是内置类型,给这个类写了一个无参的构造函数,接下来,定义了一个日期类Date,他有四个成员变量,其中_year_month_day都是内置类型,_t是自定义类型,并且,我们没有写日期类的构造函数,这意味着,在创建对象的时候,会去使用编译器生成的无参默认构造函数。

📖内置类型给默认值
C++11中针对内置类型成员不初始化的缺陷,打了补丁,即:内置类型成员变量在类中声明时可以给默认值

🎊示例:

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++编译器自动调用析构函数

小Tips:析构函数不能重载。

🎊示例:

typedef int DataType;
class Stack
{
public:
	Stack(size_t capacity = 3)
	{
		_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()//析构函数
	{
		if (_array)
		{
			free(_array);
			_array = NULL;
			_capacity = 0;
			_size = 0;
		}
	}
private:
	DataType* _array;
	int _capacity;
	int _size;
};
void TestStack()
{
	Stack s;
	s.Push(1);
	s.Push(2);
}

在这里插入图片描述

Stack中的成员变量_array_capacity_size都是内置类型,所以在对象s生命周期结束要销毁的时候,不需要资源清理,最后系统直接将其内存回收即可,而_array指向的空间是在堆区上申请的,这块空间不会随着对象生命周期的结束而自动释放(归还给操作系统),所以_array被回收后,就找不到动态申请的那块空间,会造成内存泄漏,因此在对象销毁前,要通过析构函数去释放成员变量_array指向的空间,这就是析构函数的作用。

📖编译器生成的析构函数干了什么?
我们不写,编译器会自动生成一个析构函数。该析构函数对内置类型不做处理,对自定义类型会去调用它的析构函数

🎊示例:

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;
}

在这里插入图片描述
main方法中创建了Date对象d,而d中包含4个成员变量,其中_year_month_day、三个是内置类型成员,对象销毁时不需要资源清理,最后系统直接将其内存回收即可,而_tTime类对象,在d销毁时,要将器内部包含的Time类的_t对象销毁,所以要去调用Time类的析构函数。但是main函数中不能直接调用Time类的析构函数,实际销毁的是Date类对象d,所以编译器会调用Date类的析构函数,而Date类没有显示提供,则编译器会给Date类生成一个默认的析构函数,目的是在其内部调用Time类的析构函数。

📖总结
一般情况下,有动态申请资源,就需要显式的写析构函数来释放资源,没有动态申请的资源,可以不写析构函数,需要释放资源的成员都是自定义类型,也不需要写析构函数。


🎁结语:
 今天的分享到这里就结束啦!如果觉得文章还不错的话,可以三连支持一下,您的支持就是春人前进的动力!
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

动态sql语句

1.1 动态sql语句概述 Mybatis 的映射文件中&#xff0c;业务逻辑复杂时&#xff0c; SQL是动态变化的&#xff0c;此时在前面的学习中 SQL 就不能满足要求了。 参考的官方文档&#xff1a; 1.2 动态 SQL 之<if> 根据实体类的不同取值&#xff0c;使用不同的 SQL语句…

园区能源控制管理系统

园区能源控制管理系统是一种能够实现对园区内能源消耗、供应和分配进行实时监控、管理和控制的系统。该系统通过对园区内各种能源设备的数据采集、处理和分析&#xff0c;为管理者提供实时的能源使用情况和数据分析&#xff0c;从而帮助管理者制定科学的能源管理策略和节能措施…

3Ds max图文教程:高精度篮球3D建模

推荐&#xff1a; NSDT场景编辑器助你快速搭建可二次开发的3D应用场景 第 1 步。使用以下设置在顶部视口上创建球体&#xff1a; 第 2 步。将球体转换为可编辑的多边形&#xff1a; 第 3 步。转到 Edge 子对象级别并剪切以下边缘&#xff1a; 第 4 步。选择以下边&#xff0c;然…

github 最简单的使用步骤(个人学习记录~)

github 使用步骤&#xff1a; (11条消息) github新手用法详解&#xff08;建议收藏&#xff01;&#xff01;&#xff01;&#xff09;_github详解_怪 咖的博客-CSDN博客 1.获取ssh密钥 打开输入&#xff1a;ssh-keygen -t rsa -C “git账号” 输入之后一路Enter&#xff08…

Kubernetes Service的过程

文章目录 Kubernetes Service的实现基础内容1. 命令 ip route show table all2. DNAT3. IPVS和iptables4. Service Service的实现简述 Kubernetes Service的实现 基础内容 在了解Service之前,需要先了解一些额外的知识: 命令: ip route show table allDNATIPVS和iptables基础…

【云原生】K8S单节点搭建

Kubernetes Kubernetes基础概念架构1、基础环境2、安装kubelet、kubeadm、kubectl 2、使用kubeadm引导集群1、下载各个机器需要的镜像2、初始化主节点 Kubernetes核心实战Pod Kubernetes基础概念 kubernetes具有以下特性&#xff1a; ● 服务发现和负载均衡 Kubernetes 可以使…

EasyCVR录像阈值配置未生效,是什么原因?

有用户反馈&#xff0c;在平台中设置了录像阈值不生效&#xff0c;导致磁盘爆满。针对该反馈&#xff0c;我们立即进行了排查。 EasyCVR基于云边端一体化架构&#xff0c;可支持多协议、多类型设备接入&#xff0c;在视频能力上&#xff0c;平台可实现视频直播、录像、回放、检…

小白带你学习Linux的rsync的基本操作(二十四)

目录 前言 一、概述 二、特性 1、快速 2、安全 三、应用场景 四、数据的同步方式 五、rsync传输模式 六、rsync应用 七、rsync命令 1、格式 2、选项 3、举例 4、配置文件 5、练习 八、rsyncinotfy实时同步 1、服务器端 2、开发客户端 前言 Rsync是一个开源的…

分布式应用之zookeeper集群+消息队列Kafka

一、zookeeper集群的相关知识 1.zookeeper的概念 ZooKeeper是一个分布式的&#xff0c;开放源码的分布式应用程序协调服务&#xff0c;是Google的Chubby一个开源的实现&#xff0c;是Hadoop和Hbase的重要组件。它是一个为分布式应用提供一致性服务的软件&#xff0c;提供的功能…

【深入探究人工智能】:历史、应用、技术与未来

深入探究人工智能 前言人工智能的历史人工智能的应用人工智能的技术人工智能的未来当代的人工智能产物结语&#x1f340;小结&#x1f340; &#x1f389;博客主页&#xff1a;小智_x0___0x_ &#x1f389;欢迎关注&#xff1a;&#x1f44d;点赞&#x1f64c;收藏✍️留言 &am…

软件渗透测试真的很重要吗?渗透测试有哪些测试流程?

软件渗透测试是指通过模拟恶意攻击者的行为&#xff0c;评估软件系统中的潜在安全漏洞和弱点的活动。这种安全测试方法能够帮助开发人员和系统管理员发现并修复潜在的安全漏洞&#xff0c;以确保软件系统的安全性和完整性。软件渗透测试是一项高度技术性的任务&#xff0c;需要…

Docker 基本管理

Docker 基本管理 一&#xff1a;Docker 概述1.容器化越来越受欢迎&#xff0c;因为容器是&#xff1a;2.Docker与虚拟机的区别&#xff1a;3.Docker核心概念&#xff1a; 二&#xff1a;安装 Docker1.安装依赖包2.设置阿里云镜像源3.安装 Docker-CE并设置为开机自动启动4.查看 …

Storage、正则表达式

1 LocalStorage 2 SessionStorage 3 正则表达式的使用 4 正则表达式常见规则 5 正则练习-歌词解析 6 正则练习-日期格式化 Storage-Storage的基本操作 // storage基本使用// 1.token的操作let token localStorage.getItem("token")if (!token) {console.log(&q…

SpringBoot读取配置的6种方式

1. 概述 通过了解springboot加载配置&#xff0c;可以更方便地封装自定义Starter。 在SpringBoot中&#xff0c;可以使用以下6种方式读取 yml、properties配置&#xff1a; 使用Value注解&#xff1a;读取springboot全局配置文件单个配置。使用Environment接口&#xff1a;通过…

J-Flash烧录工具如何添加新的芯片类型

0 Preface/Foreword 1 添加方法 1.1 修改JLinkDevices.xm <!-- --> <!-- CMS --> <!-- --> <Device> <ChipInfo Vendor"CMS32" Name"CMS32L051" Core"JLINK_CORE_CORTEX_…

每天一点Python——day58

#第五十八天 集合间的关系&#xff1a; 类似于数学中学到的集合一样&#xff0c;关系差不多&#xff0c;譬如相等&#xff0c;子集&#xff0c;交集 如图所示&#xff1a;#①两个集合是否相等&#xff1a;运用运算符【等号】或者运算符&#xff01;【不等号】进行判断 #例&…

企业电子招标采购系统源码Spring Cloud + Spring Boot + MybatisPlus + 前后端分离 + 二次开发

项目说明 随着公司的快速发展&#xff0c;企业人员和经营规模不断壮大&#xff0c;公司对内部招采管理的提升提出了更高的要求。在企业里建立一个公平、公开、公正的采购环境&#xff0c;最大限度控制采购成本至关重要。符合国家电子招投标法律法规及相关规范&#xff0c;以及审…

3Ds max入门教程:快捷键命令和鼠标热键大全

推荐&#xff1a; NSDT场景编辑器助你快速搭建可二次开发的3D应用场景 起初&#xff0c;您可能需要花一些时间查找热键&#xff0c;但是一旦您使用它们记住了它们&#xff0c;它们将使您以至少两倍的速度产生结果。例如&#xff0c;当您进行建模&#xff08;移动、缩放、旋转等…

allure环境搭建

allure环境搭建 在搭建之前你应该有python、pycharm allure介绍 官网&#xff1a;https://docs.qameta.io/allure/ 英文介绍 Allure Framework is a flexible lightweight multi-language test report tool that not only shows a very concise representation of what have…

青岛大学_王卓老师【数据结构与算法】Week05_13_队列的顺序表示和实现1_学习笔记

本文是个人学习笔记&#xff0c;素材来自青岛大学王卓老师的教学视频。 一方面用于学习记录与分享&#xff0c; 另一方面是想让更多的人看到这么好的《数据结构与算法》的学习视频。 如有侵权&#xff0c;请留言作删文处理。 课程视频链接&#xff1a; 数据结构与算法基础…