c++编程(4)——类与对象(2)构造函数、析构函数

news2024/12/29 6:58:26

欢迎来到博主的专栏——c++编程
博主ID:代码小豪

文章目录

    • 构造函数
    • 默认构造函数
    • 析构函数
    • 默认析构函数

构造函数

以一个日期类为例。

class Data {
private:
	int _year;
	int _month;
	int _day;
};

这个类实例化出来的对象内的值都是编译器默认初始化的随机值。

	Data d1;//d1是Data类实例化出来的对象

在这里插入图片描述
如果我们想要将对象内部的值进行初始化,通常会使用以下两种方案。

方案1,设计一个初始化的成员函数。当对象创建后,调用这个初始化函数完成对象的初始化。

class Data {
public:
	void DtatInit(int year=2024, int month=4, int day=13)//初始化成员变量的函数
	{
		_year = year;
		_month = month;
		_day = day;
	}

private:
	int _year;
	int _month;
	int _day;
};

当每个对象创建后,就调用这个初始化函数,完成对象的初始化。

	Data d1;
	Data d2;
	d1.DtatInit();//初始化d1
	d2.DtatInit(2004, 2, 14);//初始化d2

在这里插入图片描述

但是这种方法有一个缺点,对象的初始化需要依靠一个初始化的函数,这就会导致使用者偶尔会在创建对象之后忘记将这个对象初始化,导致程序崩溃。这是完全有可能的,因为编译器不会对对象没有初始化而报错,这就需要程序员花费精力去程序当中找寻出错的原因。而在多人合作编程的情况下,这种问题会越发显著。

因此c++推出类的构造函数,构造函数能在对象的创建时自动调用,构造函数有以下特点:

(1)函数名与类名相同
(2)无返回值
(3)对象实例化时自动调用构造函数
(4)构造函数可以重载
(5)构造函数不可以在对象创建之后调用

构造函数中的无返回值并不是函数返回类型为void,而是不使用任何返回类型作为函数的返回值。
还是以Data类为例。

构造函数的声明要符合函数名与类名相同,且无返回值。因此Data类构造函数如下:

class Data {
public:
	Data(int year,int month,int day)//构造函数
	{
		_year = year;
		_month = month;
		_day = day;
	}
	Data()//构造函数的重载函数
	{
		_year = 2024;
		_month = 4;
		_day = 13;
	}
private:
	int _year;
	int _month;
	int _day;
};

由于构造函数支持重载,因此我们可以定义多种类型的构造函数。如上图,其中一个构造函数有参数,另一个构造函数没有参数,没有参数的构造函数是对象创建时默认调用的构造函数。使用方式如下:

	Data d1(2004,2,14);//自动调用有参数的构造函数
	Data d2;//自动调用无参数的构造函数

此时Data类的两个对象会按照设定的构造函数形式进行初始化。
在这里插入图片描述
d1调用的有参的构造函数,参数为year=2004,month=2,day=14.因此d1的初始化结构如下,而d2调用的无参构造函数,若是调用无参的构造函数,不能加上"()",否则报错。

构造函数还可以使用缺省形式,如:

class Data {
public:
	Data(int year=2024,int month=4,int day=13)
	{
		_year = year;
		_month = month;
		_day = day;
	}

private:
	int _year;
	int _month;
	int _day;
};

这样会更加灵活。

默认构造函数

无论用户有没写构造函数,在创建对象时,编译器都会自动调用构造函数。这么一说大家可能觉得有点匪夷所思,毕竟有些类是没有写构造函数的,如果没写构造函数也要调用的话,为什么编译器不报错呢?

这是因为编译器在用户没有创建构造函数时,会自动生成一个构造函数,编译器生成的构造函数规则如下:
(1)类中的内置类型成员默认初始化
(2)类中的自定义类型调用其默认构造函数

类中的内置成员(如int,double,char)的初始化根据不同的编译器会初始化成不同的值,这是由于c++标准并没有规定生成的构造函数将内置类型初始化的标准值,因此初始化的结果由编译器的创作者决定。比如vs2022中的内置类型的默认初始化的值是一个随机值:
在这里插入图片描述

类中的自定义类型调用的则是默认构造函数,那么什么是默认构造函数呢?

默认构造函数,即创建对象对象时可以无参调用的构造函数。

	Data d2;//自动调用无参数的构造函数

那么什么样的构造函数能够满足无参调用呢?
(1)无参数的构造函数
(2)全缺省的构造函数
(3)编译器生成的构造函数

以上三种构造函数都是默认构造函数。

如果函数有无参的构造函数或者全缺省的构造函数(类中只能存在无参构造函数与全缺省构造函数其中之一),那么编译器就不会生成构造函数,反之,若是类中没有定义无参构造函数和全缺省构造函数,则会调用编译器生成的构造函数。

既然使用编译器的构造函数会导致成员随机值,那么这个自动生成的构造函数很鸡肋啊。无论编译器有没有生成构造函数,自定义类型的成员值都是随机值,那么这个构造函数的作用是什么呢?

我们将目光转移到编译器生成的构造函数的第二条特性上
(2)类中的自定义类型调用其默认构造函数

类中的成员可以是其他类的对象、结构体、联合体等自定义类型的数据。那么初始化他们则不是随机值,而是调用这些自定义类型的默认构造函数。

比如在Data类中,新增一个Time类,这个Time类用来记录时分秒。

class Time {
public:
	Time(int hour=22,int min=37,int second=58)//Time的默认构造函数
	{
		_hour = hour;
		_min = min;
		_second = second;
	}
	
private:
	int _hour;
	int _min;
	int _second;
};

class Data {
private:
	int _year;
	int _month;
	int _day;
	Time t;
};

此时若是创建一个Data类型的对象,就能发现对象当中t的值被Time类中的构造函数初始化了。
在这里插入图片描述
所以这里可以得出编译器生成的构造函数的主要作用是为了初始化类中的自定义类型,而内置类型的初始化需要自己定义一个默认构造函数,这样子当别的类包含其他类时,能将其他类一起初始化。

注意这个过程是可以嵌套的,比如类a包含类b,类b包含类c,那么创建a对象时,调用b的默认构造函数,b中又会调用c的默认构造函数。

析构函数

析构函数的作用与构造函数的作用相反,相反的点体现在以下方面:
(1)调用的时机不同:析构函数在对象销毁之后自动调用
(2)调用的目的不同:构造函数的目的是在对象创建时进行初始化,而析构函数的目的是在对象离开生命周期准备销毁时,将对象中的资源进行释放。

析构函数的特性如下:

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

析构函数的命名方式很巧妙,既然析构函数是构造函数的相反面,那么析构函数的命名规则就是在构造函数的基础之上加个取反符(~),寓意析构函数的性质与构造函数相反。

以栈类stack为例。他的析构函数如下:

typedef int STDataType;
class stack {
public:
	stack(int capacity = 4)//栈的默认构造函数
	{
		_stack = (STDataType*)malloc(sizeof(STDataType) * capacity);
		_capacity = capacity;
		_top = 0;
	}

	~stack()//栈的析构函数
	{
		free(_stack);
		_top = 0;
		_stack = NULL;
		_capacity = 0;
	}

private:
	STDataType* _stack;
	int _top;
	int _capacity;
};

不是什么类都需要析构函数的,若是类中没有动态内存开辟出来资源空间需要清理,那么这个类不需要写一个析构函数了。在stack类中,成员变量_stack中的空间是malloc出来的动态内存,这个空间是需要释放的,否则会导致内存泄漏(这里的知识涉及C语言的动态内存分配)。

因此我们可以设置一个析构函数用来及时释放空间

默认析构函数

事实上析构函数大部分情况下都使用不上参数,编译器在类中没有析构函数时,也会生成一个析构函数,编译器的析构函数不对内置类型成员产生作用(任意的指针类型都是内置类型),只会调用自定义成员调用其析构函数。

比如现在有一个嵌套stack类的类_stack.

class _stack
{
private:
	int i;
	stack s1;
};

_stack类没有设置析构函数,因此当_stack类的对象销毁时,会调用编译器生成的析构函数,而编译器生成的析构函数的作用是调用s1的默认析构函数,因此在_stack类的对象销毁时,会调用s1的默认析构函数,将s1中的空间也进行销毁。

总结:构造函数和析构函数的目的都是为了减少对象初始化,以及对象开辟的资源没有及时清理带来的麻烦。

构造函数会在对象创建之处完成成员变量的初始化,而对嵌套对象调用其构造函数又能对嵌套的对象完成初始化,以此类推,减少了初始化不正确带来的麻烦。

而析构函数也会在对象即将销毁时调用,减少了内存泄漏出现的次数

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

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

相关文章

私域流量变现干货:轻松盘活,高效增长!

你知道如何增长私域流量并将这些流量转化为实际收益,让我们的品牌价值最大化吗? 今天,就分享几点干货,帮助大家盘活私域流量,实现高效增长! 1、精准定位和用户画像 首先,了解您的私域流量源于…

位像素谈海外仓库存管理方法:WMS对效率提升到底有没有用?

对海外仓来说,最重要的事情就是把库存管理做清楚。因为海外仓的本质还是仓储,如果连仓储数据都出现错误,那不管是对客户还是对终端消费者来说,都会造成巨大的影响。 今天我们就来聊一下,在电商飞速发展的今天&#xf…

面试官:说一说CyclicBarrier的妙用!我:这个没用过...

写在开头 面试官:同学,AQS的原理知道吗? 我:学过一点,抽象队列同步器,Java中很多同步工具都是基于它的… 面试官:好的,那其中CyclicBarrier学过吗?讲一讲它的妙用吧 我&…

Sonar下启动发生错误,elasticsearch启动错误

Download | SonarQube | Sonar (sonarsource.com) 1.首先我的sonar版本为 10.4.1 ,java版本为17 2.sonar启动需要数据库,我先安装了mysql, 但是目前sonar从7.9开始不支持mysql,且java版本要最少11,推荐使用java17 3.安装postsql,创建sonar数据库 4.启…

什么是5G单兵?5G单兵跟5G执法记录仪有什么异同?

深入了解5G单兵系统:未来执法新革命与执法记录仪的差异分析 随着技术的不断进步和发展,5G技术的应用已经渗透到社会的各个领域中,包括公安、消防等执法与紧急救援领域。其中,5G单兵系统作为一种新型的智能化执法装备,…

树形侧边栏(展开、全选、切换名称)

父文件&#xff1a; index.vue <template><div class"h-full p20px bg-#f5f5f5"><ContentWrap class"w-260px h-[calc(100vh-200px)] min-h-700px"><TenantTree select"tentantSelect" /></ContentWrap></div&…

【计算机考研】跨考计算机,需要准备多久才来得及?

9个月跨考计算机&#xff0c;如果选择是408的话&#xff0c;时间稍微有点紧张&#xff0c;前期感觉不大&#xff0c;后期数学408堆在一起会感觉很难受... 很多确定考408的同学都是一开始先从数据结构开始复习的&#xff0c;这样到了中后期觉得自己时间不够了再去转自命题也来得…

MySQL——基础

SQL 全称 Structured Query Language&#xff0c;结构化查询语言。操作关系型数据库的编程语言&#xff0c;定义了一套操作关系型数据库统一标准 。 SQL 通用语法 SQL语句可以单行或多行书写&#xff0c;以分号结尾。SQL语句可以使用空格/缩进来增强语句的可读性。MySQL数据库…

【C++】unordered_map unordered_set 底层刨析

文章目录 1. 哈希表的改造2. unordered_map3. unordered_set C STL 库中&#xff0c;unordered_map 和 unordered_set 容器的底层为哈希表&#xff0c;本文将简单模拟哈希表&#xff08;哈希桶&#xff09;&#xff0c;unordered_map 和 unordered_set 只需封装哈希表的接口即可…

分布式的计算框架之Spark(python第三方库视角学习PySpark)

基本介绍 Apache Spark是专为大规模数据处理而设计的快速通用的计算引擎 。现在形成一个高速发展应用广泛的生态系统。 特点介绍 Spark 主要有三个特点&#xff1a; 首先&#xff0c;高级 API 剥离了对集群本身的关注&#xff0c;Spark 应用开发者可以专注于应用所要做的计…

C语言面试题之奇偶链表

奇偶链表 实例要求 1、给定单链表的头节点 head &#xff0c;将所有索引为奇数的节点和索引为偶数的节点分别组合在一起&#xff0c;然后返回重新排序的列表&#xff1b;2、第一个节点的索引被认为是 奇数 &#xff0c; 第二个节点的索引为 偶数 &#xff0c;以此类推&#x…

快速掌握Spring监控(Spring Boot admin)

监控 监控可视化监控平台Admin底层逻辑info 自定义端点 监控 监控的作用&#xff1a; 监控服务状态是否宕机监控服务运行指标&#xff08;内存&#xff0c;虚拟机&#xff0c;线程&#xff0c;请求等&#xff09;监控日志管理服务&#xff08;服务下线&#xff09; 监控的实…

详解IP证书申请

申请IP证书&#xff0c;也被称为IP SSL证书&#xff0c;是一种特殊的SSL证书&#xff0c;它不同于传统的域名验证&#xff08;DV&#xff09;证书&#xff0c;是通过验证公网IP地址而不是域名来确保安全连接。这种证书用于保护IP地址&#xff0c;并在安装后起到加密作用。以下是…

李廉洋:4.16黄金,原油最新资讯,亚盘面走势分析及策略。

周一&#xff0c;10年期美国国债收益率攀升3个基点&#xff0c;至4.55%&#xff0c;原因是投资者在权衡中东紧张局势加剧演变为全面地区战争的风险。美国国债将成为市场关注的焦点&#xff0c;上周美国国债收益率上升12个基点&#xff0c;至去年11月以来的最高水平&#xff0c;…

TC387实现SPI自通讯

TC387实现SPI自通讯 预期效果&#xff1a; TC387上定义两个SPI通讯接口&#xff0c;一个用于发数据一个用于收数据。准确无误的收到一次数据就对核心板led灯的状态进行一次翻转。 由于实验设备有限&#xff0c;只能想办法通过现有设备进行实验。 实现过程&#xff1a; 最开…

【学习笔记PPT摘录】lan.289.24.4-15

1.纪念品分组.双指针-01 #include <bits/stdc++.h> using namespace std;int A[40000];// 纪念品价值均衡// 把购来的纪念品进行分组 之和不超过整数 w// 每组只能有两个纪念品 希望分组的数目要少// 贪心的策略就是 每个较大的数找到一个 最大的较小的数使其能够小于w//…

基于注解以及配置类使用SpringIoc

四 基于注解方式使用SpringIoc 和 XML 配置文件一样&#xff0c;注解本身并不能执行&#xff0c;注解本身仅仅只是做一个标记&#xff0c;具体的功能是框架检测到注解标记的位置&#xff0c;然后针对这个位置按照注解标记的功能来执行具体操作。 本质上&#xff1a;所有一切的…

京东商品详情API接口(商品属性丨sku价格丨详情图丨标题等数据)

京东商品详情API接口是京东开放平台提供的一种API接口&#xff0c;通过调用该接口&#xff0c;开发者可以获取京东商品的标题、价格、库存、月销量、总销量、详情描述、图片等详细信息。下面针对您提到的商品属性、SKU价格、详情图以及标题等数据&#xff0c;做具体介绍&#x…

c++ 多文件编程

1.结构目录 声明类:用于声明方法,方便方法管理和调用&#xff1b; 实现类:用于实现声明的方法; 应用层:调用方法使用 写过java代码的兄弟们可以这么理解&#xff1a; 声明类 为service层 实现类 为serviceimpl层 应用层 为conlloter层 2.Dome 把函数声明放在头文件xxx.h中&…

STM32学习和实践笔记(5):时钟树

STM32一共有4个时钟源。外部时钟高低速各一个&#xff0c;内部时钟高低速各一个。 外部高速时钟是&#xff1a;4-16MHZ的HSE OSC。HS表示高速high speed. E表示外部的external。开发板该处安装的8M晶振。 外部低速时钟是&#xff1a;32.768KHz的LSI OSC。LS表示高速low speed…