类与对象(2)

news2025/1/11 6:11:46

✨前言✨

📘 博客主页:to Keep博客主页
🙆欢迎关注,👍点赞,📝留言评论
⏳首发时间:2023年11月11日
📨 博主码云地址:博主码云地址
📕参考书籍:《C++ Primer》《C++编程规范》
📢编程练习:牛客网+力扣网
由于博主目前也是处于一个学习的状态,如有讲的不对的地方,请一定联系我予以改正!!!

类中的六个默认成员函数
在这里插入图片描述

文章目录

  • 构造函数
  • 析构函数
  • 拷贝构造
  • 赋值运算符重载
  • const成员函数
  • 取地址操作符重载

构造函数

构造函数的作用:在创建对象时,同时帮助我们给对象一个初始值,并不是给对象开辟空间

构造函数的定义如下:
1️⃣ 函数名与类名一样
2️⃣ 构造函数是不带返回值的
3️⃣ 构造函数支持重载
4️⃣ 对象进行实例化时,编译器会自动调用构造函数

让我们来看一看以下这段代码,来看一下如何使用构造函数:

class Date {
private:
	int _year;
	int _month;
	int _day;

public:
	Date() {
		_year = 2023;
		_month = 11;
		_day = 2;
	}
	Date(int year, int month, int day) {
		_year = year;
		_month = month;
		_day = day;
	}
	void print()
	{
		cout << _year << _month << _day << endl;
	}
};

int main()
{
	Date d1();//编译器会当做是函数的声明,是要去调用函数
	Date d2;//调用Date类的不带参数构造函数
	d2.print();

	Date d3(2024, 11, 2);//调用有参数的构造函数
	d3.print();
	return 0;
}

如果自己不写构造函数,编译器也是会生成默认不带参数的构造函数!默认的构造函数有什么作用呢,让我们来看下面这段代码来进一步的理解:

class people {
private:
	int _age;
	double _height;
	double _weight;

public:
	people() {
		_age = 1;
		_height = 1;
		_weight = 1;
	}

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

	people p;

public:

	void print()
	{
		cout << _year<<" " << _month<<" "<< _day << endl;
	}
};

int main()
{
	Date d1();//编译器会当做是函数的声明,是要去调用函数
	Date d2;//调用Date类的默认构造
	d2.print();
}

在这里插入图片描述
我们可以发现,在d2这个对象中_year等是一个随机值,对于people这个类型的变量p是可以进行初始化的!事实上,在C++中,int/char/指针…等,也就是对于哪些可以直接拿来用的类型我们称为内置类型,像利用union/struct/class等关键字定义的类型,我们称为自定义类型。对于内置类型,构造函数是不进行处理的,对于自定义类型,是会去调用这个类型的默认构造!

编译器默认生成的无参构造函数,用户自己写的不带参数的默认构造函数,以及全缺省的构造函数我们都称之为默认的构造函数,而且一旦我们自己显式的写了构造函数,编译器就不会在生成构造函数了!

在C++11中,打了一个补丁,可以针对内置成员变量给定一个缺省值!注意此时成员变量仍然是一个定义,并没有因为给定这个缺省值就初始化了一个空间!

析构函数

与构造函数功能相反,析构函数的作用就是对象销毁时,自动调用析构函数,从而完成对象中的资源清理工作
语法格式如下:

1️⃣ 也是与类名相同的函数,函数名前面要加上~
2️⃣ 析构函数同样也是无参数也无返回值
3️⃣ 析构函数不能构成重载,一个类中只可以有一个析构函数,若用户显式写出,编译器就不在生成
4️⃣ 析构函数会在对象生命周期结束后,自动调用!
结合下列代码:

#include <iostream>
using namespace std;

class Date {
private:
	int _year;
	int _month;
	int _day;
public:
	Date()
	{
		_year = 2023;
		_month = 11;
		_day = 2;
	}
	//析构函数
	~Date()
	{
		cout << "Date()" << endl;
	}

	void print()
	{
		cout << _year << " " << _month << " " << _day << endl;
	}
};

class Stack {
	int* _x;
	int _top;
	int _capacity;
public:
	Stack(int capacity = 3) 
	{
		_x = (int*)malloc(sizeof(int) * capacity);
		if (_x == nullptr)
		{
			cout << "申请空间失败" << endl;
			return;
		}
		_capacity = capacity;
		_top = 0;
	}
	void print()
	{
		cout << _top << " " << _capacity << endl;
	}
	~Stack()
	{
		free(_x);
		_top = _capacity = 0;
		_x = nullptr;
	}
};
int main()
{
	Date d1;
	Stack st1;
	return 0;
}

析构函数类似于构造函数,对于内置类型,是不做处理的,对于自定义类型,是会去调用它的析构函数进行处理!对于我们之前所学的栈,一般就需要我们自己写析构函数,因为栈需要向系统申请资源开辟空间!这时我们需要利用析构函数,将这块资源进行清理,防止资源泄露!Date类可以使用默认的析构函数,Date类中全是内置类型,没有向系统申请空间!
图解如下:
在这里插入图片描述

拷贝构造

在C语言学习结构体中,我们知道,是可以传值传参的。在C++中,那么对于自定义类型进行传值传参会发生什么呢?

class Date {
private:
	int _year;
	int _month;
	int _day;
public:
	Date()
	{
		_year = 2023;
		_month = 11;
		_day = 2;
	}
	//析构函数
	~Date()
	{
		cout << "Date()" << endl;
	}

	void print()
	{
		cout << _year << " " << _month << " " << _day << endl;
	}
};

class Stack {
	int* _x;
	int _top;
	int _capacity;
public:
	Stack(int capacity = 3) 
	{
		_x = (int*)malloc(sizeof(int) * capacity);
		if (_x == nullptr)
		{
			cout << "申请空间失败" << endl;
			return;
		}
		_capacity = capacity;
		_top = 0;
	}
	~Stack()
	{
		free(_x);
		_top = _capacity = 0;
		_x = nullptr;
	}
};
void func(Date a)
{
	a.print();
}
void func1(Stack st1)
{
	
}
int main()
{
	Date x;
	func(x);

	Stack st;
	func1(st);
	return 0;
}

运行代码,我们可以发现会报错!这是为什么呢?这是因为在进行传值传参的过程中,我们进行的只是值拷贝(浅拷贝),而在清理对象资源的时候,会自动调用析构函数,而对于Stack这样的类,同一块空间是会被释放两次!
上述代码图解过程:
st和st1所指的空间被析构了两次
在C++中,为了解决上述问题,规定,自定义类型在传值传参必须先调用拷贝构造函数!

拷贝构造函数的特点:
1️⃣ 无参数无返回值,函数名与类名相同,与构造函数构成重载
2️⃣ 函数的参数只有一个,是类类型的引用,不可以使用传值方式

在这里需要特别说明一下为什么拷贝构造函数不可以使用传值方式!先假设拷贝构造是传值传参的,调用函数进行传值传参需要先调用拷贝构造,而拷贝构造又是传值传参,又需要接着调拷贝构造……就这样套娃下去,形成一个无限递归!
改进代码如下:

//拷贝构造
Stack(const Stack& t)
{
	_x = (int*)malloc(sizeof(int) * t._capacity);
	if (_x == nullptr)
	{
		perror("malloc fail");
	}
	_capacity = t._capacity;
	_top = t._top;
	memcpy(_x, t._x,sizeof(int)*t._capacity);
}

只需要在Stack类中重新写构造函数,实现对资源的拷贝,这样就不会存在对一块空间释放两次的问题!

其实对于Date这样的类,就算我们使用默认生成的拷贝构造,也是可以的,因为Date类中没有向系统申请资源!而对于Stack这样的类就必须我们自己实现,因为它向系统申请开辟了空间,我们需要拷贝这块空间的内容!同时,为了防止拷贝过程中,改变引用中的内容,我们通常会加上const修饰!

class myqueue
{
	Stack _pushs;
	Stack _pops;
	int _size;
};
int main()
{
	myqueue q1;
	myqueue q2(q1);
	return 0;
}

在上面的代码基础上,我们定义一个myqueue类,通过打断点按F11可以发现:拷贝构造对于自定义类型会去调用它的拷贝构造,对于内置类型会完成值拷贝!
学习了拷贝构造,我们可以来看以下这段代码,再来理解一下引用的用途:

Date test()
{
	Date x;
	x.print();
	return x;
}
int main()
{
	return 0;
}

你觉得返回的是x还是x拷贝的临时对象呢?实际上,x出了test函数就会销毁,所以只可以返回x的拷贝!如果我们写出如下的代码:

Date& test()
{
	static Date x;
	x.print();
	return x;
}
int main()
{
	return 0;
}

此时,由于x出了作用域还没有销毁!所以我们可以通过传引用返回!这样会减少拷贝对象的时间,是不是会大大提高效率呢?

赋值运算符重载

我们知道,内置类型的比较是可以直接用符号来进行比较的!C++为了增强代码的可读性引入了运算符重载的!运算符重载是具有特殊函数名的函数!

函数名字:operator 后面接需要重载的运算符符号()

下面来展示一段代码:

class Date {
public:
	int _year;
	int _month;
	int _day;
public:
	Date()
	{
		_year = 2023;
		_month = 11;
		_day = 2;
	}
	void print()
	{
		cout << _year << " " << _month << " " << _day << endl;
	}
};


bool operator==(Date& x,Date& y)
{
	return x._year == y._year
		&& x._month == y._month
		&& x._day == y._day;
}

int main()
{
	Date d1;
	Date d2;

	cout << (d1==d2) << endl;
	return 0;
}

为了便于理解,我们暂时先将类的成员改为public,我们通过operator关键字重载==符号,这样我们就可以像内置类型一样,通过 符号来判断是否相等! 在C++中,对于运算符重载,我们一般是放入类中处理,但是传参就要少一个了,因为有一个隐藏的this指针!具体代码可以参考下面:

	bool operator==( Date& y)
	{
		return _year == y._year
			&& _month == y._month
			&& _day == y._day;
	}

此外我们在介绍两个特殊的运算符重载:

	//前置++运算符重载
	Date& operator++()
	{
		*this += 1;
		return *this;
	}
	//后置++运算符重载
	Date operator++(int)
	{
		Date tmp(*this);
		*this += 1;
		return tmp;
	}

对于后置++运算符,我们通过给定一个整型来表示这是后置++(注意这个是语法规定)
编译器自己会根据我们所写的符号去调用相应的运算符重载函数(这个是需要我们自己写的)!编译器默认生成的成员函数是赋值运算符的重载,也就是说我们自己不写,也是会自动生成的!

	//赋值赋值运算符重载
	Date& operator=(const Date& y) //返回值的目的是为了进行连续赋值,引用可以提高返回的效率
	{
		//防止自己和自己赋值
		if (this != &y)
		{
			_year = y._year;
			_month = y._month;
			_day = y._day;
		}
		return *this;
	}

和拷贝构造的作用一样,对于内置类型会进行值拷贝(浅拷贝),自定义类型调用它的赋值重载运算符!但是对于Stack这样的类,向系统申请了额外资源的需要我们自己重写赋值重载实现深拷贝!

对于运算符重载需要注意事项:
1️⃣ 对于内置类型运算符的含义不可以改变。例如:不能重载+符号,里面写成了-的含义了
2️⃣ 不可以通过连接其他符号创建新的操作符。例如:opertor@
3️⃣ 重载操作符必须要有一个类类型的参数
4️⃣ 作为类的成员函数重载时,其形参看起来比操作数数目少1,因为成员函数第一个参数是隐藏的this指针
5️⃣.* ::(域限定符) sizeof ?:(三目操作符) .这五个运算符是不可以运算符重载的!

const成员函数

被const修饰的成员函数被称为const成员函数!

	//大于重载
	bool operator>(Date& t)const //在函数参数后面加上const修饰
	{
		if (_year > t._year)
		{
			return true;
		}
		else if (_year == t._year && _month > t._month) {
			return true;
		}
		else if (_year == t._year && _month == t._month && _day == t._day)
		{
			return true;
		}
		return false;
	}

实际上const修饰的是隐藏的this指针,变成了const Date类型,表明在该成员函数中不能对类的任何成员修改!还需要注意的是当自定义类型的声明与定义分离时,我们如果要用const进行修饰,那么就要声明和定义一起加上!
在这里插入图片描述
当我们定义const对象和非const对象时,调用>重载运算符如图所示!这是为啥呢?原因就是d1是const Date
类型对象,传入>重载运算符时第二个参数是Date*类型!可以理解为权限放大,这样是不允许的!权限只可以平移和缩小!

取地址操作符重载

最后两个默认的成员函数是取地址操作符的重载

	Date* operator&()
	{
		return this;
	}

	const Date* operator&()const
	{
		return this;
	}

一般这两个默认的成员函数不需要我们自己进行重载,直接拿来用就可以了!如果我们想要让别人获取指定的内容可以使用!

//让你只可以拿到地址为0x11223344的内容
Date* operator&()
{
	return (Date*)0x11223344;
}

const Date* operator&()const
{
	return (const Date*)0x11223344;
}

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

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

相关文章

HaaS510开板式DTU真机连云:上报监测数据至阿里云物联网平台

背景 HaaS: Hardware as a Service。 HAAS510 是一种开板式 DTU &#xff0c;旨在为用户已开发好的设备快速增加 4G 连云能力的 4G CAT1 数传模块。它通过将模组与用户设备集成到一个外壳内&#xff0c;既保持设备的一体性&#xff0c;又降低重新开发 PCB 的时间消耗和模组开…

canvas 简单直线轨迹运动与线性插值计算

canvas 简单直线轨迹运动与线性插值计算 一、canvas 直线轨迹运行 添加 canvas 语法提示 通过/** type {HTMLCanvasElement} */代码块 来添加canvas的语法提示 <body><canvas id"canvas"></canvas> </body> <script>/** type {HTM…

区块链探秘:从基础到深度,全面解读区块链技术与应用

1.区块链基本概念 1.发展历史 比特币诞生&#xff1a; 2008年&#xff0c;化名为中本聪的人发表了论文《Bitcoin&#xff1a;A Peer-to-Peer Electronic Cash System》 2009年1月3日&#xff0c;中本聪开发运行了比特币客户端程序并进行了首次挖矿&#xff0c;获得了第一批…

进阶课6——基于Seq2Seq的开放域生成型聊天机器人的设计和开发流程

情感聊天机器人通常属于开放领域&#xff0c;用户可以与机器人进行各种话题的互动。例如&#xff0c;微软小冰和早期的AnswerBus就是这种类型的聊天机器人。基于检索的开放领域聊天机器人需要大量的语料数据&#xff0c;其开发流程与基于任务型的聊天机器人相似&#xff0c;而基…

4面百度软件测试工程师的面试经验总结

没有绝对的天才&#xff0c;只有持续不断的付出。对于我们每一个平凡人来说&#xff0c;改变命运只能依靠努力幸运&#xff0c;但如果你不够幸运&#xff0c;那就只能拉高努力的占比。 2023年7月&#xff0c;我有幸成为了百度的一名测试工程师&#xff0c;从外包辞职了历经100…

解决Scrapy爬虫多线程导致抓取错乱的问题

目录 一、概述 二、问题分析 三、解决方案 四、案例分析 五、总结 一、概述 Scrapy是一个流行的Python爬虫框架&#xff0c;可以轻松地抓取网页数据并对其进行解析。然而&#xff0c;在抓取过程中&#xff0c;如果使用多线程进行并发处理&#xff0c;可能会遇到数据抓取错…

TCP触发海康扫码相机S52CN-IC-JQR-NNN25

PC环境设置 为保证客户端正常运行以及数据传输的稳定性&#xff0c;在使用客户端软件前&#xff0c;需要对 PC 环境 进行设置 关闭防火墙 操作步骤如下&#xff1a; 1. 打开系统防火墙。 2. 在自定义设置界面中&#xff0c;选择关闭防火墙的对应选项&#xff0c;并单击…

Linux学习教程(第一章 简介)1

Linux 系统已经渗透到 IT 领域的各个角落&#xff0c;作为一名 IT 从业人员&#xff0c;不管你是专注于编程&#xff0c;还是专注于运维&#xff0c;都应该对 Linux 有所了解&#xff0c;甚至还要深入学习&#xff0c;掌握核心原理。 Linux 学习建议&#xff1a;一定要安装 Li…

JSON可视化管理工具JSON Hero

本文软件由网友 zxc 推荐&#xff1b; 什么是 JSON Hero &#xff1f; JSON Hero 是一个简单实用的 JSON 工具&#xff0c;通过简介美观的 UI 及增强的额外功能&#xff0c;使得阅读和理解 JSON 文档变得更容易、直观。 主要功能 支持多种视图以便查看 JSON&#xff1a;列视图…

【斗罗二】霍雨浩拿下满分碾压戴华斌,动用家族力量,海神阁会议

Hello,小伙伴们&#xff0c;我是小郑继续为大家深度解析国漫资讯。 深度爆料《绝世唐门》第23话最新预告分析&#xff0c;魂兽升学考试中一场白虎魂师戴华斌与千年级别的风虎的决斗即将上演。风虎&#xff0c;作为虎类魂兽的王者&#xff0c;其强大的实力和独特的技能让这场战…

现在学编程还能够月薪过万吗?

当我们谈到职业选择时&#xff0c;一项常受人关注的问题是&#xff0c;现在学编程还能月薪过万吗&#xff1f;这似乎是一个不断挂在年轻人嘴边的问题&#xff0c;尤其是在数字化时代&#xff0c;编程的需求越来越大。 所以今天让我们一起探讨这个问题&#xff0c;看看现实生活…

漫谈广告机制设计 | 万剑归宗:聊聊广告机制设计与收入提升的秘密(1)

小时候看武侠电视剧《风云》的时候&#xff0c;其中无名有一招叫“万剑归宗”&#xff0c;乃是剑术最高境界。修炼的口诀是“万气自生&#xff0c;剑冲废穴&#xff1b;归元武学&#xff0c;宗远功长”&#xff0c;也就是说欲练此功&#xff0c;先自废武功&#xff0c;然后回归…

HIT_OS_LAB3 操作系统的引导

操作系统实验三 3.1. 实验目的 熟悉实验环境&#xff1b;建立对操作系统引导过程的深入认识&#xff1b;掌握操作系统的基本开发过程&#xff1b;能对操作系统代码进行简单的控制&#xff0c;揭开操作系统的神秘面纱。 3.2. 实验内容 3.2.1. 改写 bootsect.s 主要完成如下功…

如何下载Linux源码,看这篇就够了!

文章目录 前言一、linux官网二、查找发布版本三、下载方式 前言 在工作中&#xff0c;我们难免会遇到需要去找某个版本的linux源码的情况&#xff0c;今天这篇文章就手把手教大家如何找到自己想要的linux源码版本 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例…

医学影像系统源码(MRI、CT三维重建)

一、MRI概述 核磁共振成像&#xff08;英语&#xff1a;Nuclear Magnetic Resonance Imaging&#xff0c;简称NMRI&#xff09;&#xff0c;又称自旋成像&#xff08;英语&#xff1a;spin imaging&#xff09;&#xff0c;也称磁共振成像&#xff08;Magnetic Resonance Imag…

大数据Doris(二十):数据导入(Broker Load)介绍

文章目录 数据导入(Broker Load)介绍 一、​​​​​​​适用场景

docker通过nginx代理tomcat-域名重定向

通过昨天的调试&#xff0c;今天做这个域名就简单了&#xff0c; 正常我们访问网站一般都是通过域名比如&#xff0c;www.baidu.com对吧&#xff0c;有人也通过ip&#xff0c;那么这个怎么做呢&#xff1f;物理机windows可以通过域名访问虚拟机linux的nginx代理转向tomcat服务…

6.5对称二叉树(LC101-E)

算法&#xff1a; 其实就是比较左右子树是否可以翻转 比较的时候&#xff1a; 比较外面的节点是否相等&#xff0c;如示例1中的节点3 比较里面的节点是否相等&#xff0c;如示例1中的节点4 基本思路是这样的&#xff0c;那怎么遍历呢&#xff1f; 二叉树的题一定要掌握到…

开放式耳机井喷阶段,南卡品牌何以成为“头号玩家”?

对于耳机行业而言,过去两年的聚光灯属于开放式耳机这一才闯入赛道的品类。 在消费电子寒冬持续蔓延的时间节点,夺下属于自己的高光时刻实属不易,毕竟无论是过去数年间高歌猛进的智能可穿戴设备行业,还是更垂直的耳机赛道,现阶段均面临承压的局面。 一面是“前辈”们昔日的“爆…

HTB——常见端口及协议总结

文章目录 一、 常见端口二、HTTP协议三、FTP四、SMB 一、 常见端口 http协议&#xff1a;80、8000https协议&#xff1a;443、8443ftp协议&#xff1a;20&#xff08;数据传输&#xff09;、21&#xff08;发送命令&#xff09;smb协议&#xff1a;445 二、HTTP协议 https的…