【C++从0到王者】第三站:类和对象(中)赋值运算符重载

news/2024/7/3 13:02:52 标签: c++, 开发语言, c, 类和对象, 重载
cle class="baidu_pl">
cle_content" class="article_content clearfix">
content_views" class="markdown_views prism-tomorrow-night"> cap="round" d="M5,0 0,2.5 5,5z" id="raphael-marker-block" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);">

class="toc">

文章目录

  • 一、运算符重载
  • 二、赋值运算符重载
    • 1.复制拷贝与拷贝构造
    • 2.赋值运算符重载的格式
    • 3.赋值运算符重载的实现
    • 4.赋值运算符重载的注意事项

一、运算符重载

1.运算符重载的使用

当我们实现一个日期类的时候࿰c;我们有时候需要比较两个类的大小。我们可以写一个普通的函数去进行比较࿰c;但是这样比较繁琐࿰c;我们能否像内置类型那样只需要使用比较符号来进行判断呢?其实是可以的࿰c;C++为什么支持内置类型的比较呢?这是因为内置类型的都是祖师爷知道的࿰c;祖师爷知道他们如何进行比较࿰c;所以将内置类型的比较方法早已写入库中࿰c;而自定义类型祖师爷是不知道的࿰c;所以需要我们自己来实现࿰c;我们在实现的时候为了可以使用普通的比较符号࿰c;我们可以使用运算符重载

<code class="prism language-cpp">class="token keyword">class class="token class-name">Date
class="token punctuation">{
class="token keyword">publicclass="token operator">:
	class="token function">Dateclass="token punctuation">(class="token keyword">int year class="token operator">= class="token number">2023class="token punctuation">, class="token keyword">int month class="token operator">= class="token number">5class="token punctuation">, class="token keyword">int day class="token operator">= class="token number">10class="token punctuation">)
	class="token punctuation">{
		_year class="token operator">= yearclass="token punctuation">;
		_month class="token operator">= monthclass="token punctuation">;
		_day class="token operator">= dayclass="token punctuation">;
	class="token punctuation">}

class="token comment">//private:
	class="token keyword">int _yearclass="token punctuation">;
	class="token keyword">int _monthclass="token punctuation">;
	class="token keyword">int _dayclass="token punctuation">;

class="token punctuation">}class="token punctuation">;
class="token keyword">bool class="token keyword">operatorclass="token operator"><class="token punctuation">(class="token keyword">const Dateclass="token operator">& x1class="token punctuation">, class="token keyword">const Dateclass="token operator">& x2class="token punctuation">)
class="token punctuation">{
	class="token keyword">if class="token punctuation">(x1class="token punctuation">._year class="token operator">< x2class="token punctuation">._yearclass="token punctuation">)
	class="token punctuation">{
		class="token keyword">return class="token boolean">trueclass="token punctuation">;
	class="token punctuation">}
	class="token keyword">else class="token keyword">if class="token punctuation">(x1class="token punctuation">._year class="token operator">== x2class="token punctuation">._year class="token operator">&& x1class="token punctuation">._month class="token operator">< x2class="token punctuation">._monthclass="token punctuation">)
	class="token punctuation">{
		class="token keyword">return class="token boolean">trueclass="token punctuation">;
	class="token punctuation">}
	class="token keyword">else class="token keyword">if class="token punctuation">(x1class="token punctuation">._year class="token operator">== x2class="token punctuation">._year class="token operator">&& x1class="token punctuation">._month class="token operator">== x2class="token punctuation">._month class="token operator">&& x1class="token punctuation">._day class="token operator">< x2class="token punctuation">._dayclass="token punctuation">)
	class="token punctuation">{
		class="token keyword">return class="token boolean">trueclass="token punctuation">;
	class="token punctuation">}
	class="token keyword">return class="token boolean">falseclass="token punctuation">;
class="token punctuation">}
class="token keyword">int class="token function">mainclass="token punctuation">(class="token punctuation">)
class="token punctuation">{
	Date class="token function">d1class="token punctuation">(class="token number">2023class="token punctuation">, class="token number">5class="token punctuation">, class="token number">11class="token punctuation">)class="token punctuation">;
	Date class="token function">d2class="token punctuation">(class="token number">2023class="token punctuation">, class="token number">5class="token punctuation">, class="token number">12class="token punctuation">)class="token punctuation">;
	cout class="token operator"><< class="token punctuation">(d1 class="token operator">< d2class="token punctuation">) class="token operator"><< endlclass="token punctuation">;
	cout class="token operator"><< class="token keyword">operatorclass="token operator"><class="token punctuation">(d1class="token punctuation">, d2class="token punctuation">) class="token operator"><< endlclass="token punctuation">;
	cout class="token operator"><< class="token punctuation">(d2 class="token operator">< d1class="token punctuation">) class="token operator"><< endlclass="token punctuation">;
	cout class="token operator"><< class="token keyword">operatorclass="token operator"><class="token punctuation">(d2class="token punctuation">, d1class="token punctuation">) class="token operator"><< endlclass="token punctuation">;
	class="token keyword">return class="token number">0class="token punctuation">;
class="token punctuation">}
code>

像上面代码中的d1<d2事实上就被转化为了operator<(d1,d2)࿰c;我们可以从指令的角度来进行观察:可以发现其实是一样的
c="https://img-blog.csdnimg.cn/8e1e27aa476f45a19be99ebfedca1c50.png" alt="在这里插入图片描述" />

但是上面的代码仍然不够完善࿰c;因为他是定义在类外的࿰c;必须将私有成员变量给变为公有的࿰c;如果想要使用私有的࿰c;有两种方案:1.使用友元࿰c;但是这样会破坏封装࿰c;2.直接定义在类内࿰c;这样的话我们需要注意一点的是࿰c;在内里的变量是有一个this指针的࿰c;所以我们的形参数其实是需要少一个的

<code class="prism language-cpp">	class="token keyword">bool class="token keyword">operatorclass="token operator"><class="token punctuation">(class="token keyword">const Dateclass="token operator">& xclass="token punctuation">)
	class="token punctuation">{
		class="token keyword">if class="token punctuation">(_year class="token operator">< xclass="token punctuation">._yearclass="token punctuation">)
		class="token punctuation">{
			class="token keyword">return class="token boolean">trueclass="token punctuation">;
		class="token punctuation">}
		class="token keyword">else class="token keyword">if class="token punctuation">(_year class="token operator">== xclass="token punctuation">._year class="token operator">&& _month class="token operator">< xclass="token punctuation">._monthclass="token punctuation">)
		class="token punctuation">{
			class="token keyword">return class="token boolean">trueclass="token punctuation">;
		class="token punctuation">}
		class="token keyword">else class="token keyword">if class="token punctuation">(_year class="token operator">== xclass="token punctuation">._year class="token operator">&& _month class="token operator">== xclass="token punctuation">._month class="token operator">&& _day class="token operator">< xclass="token punctuation">._dayclass="token punctuation">)
		class="token punctuation">{
			class="token keyword">return class="token boolean">trueclass="token punctuation">;
		class="token punctuation">}
		class="token keyword">return class="token boolean">falseclass="token punctuation">;
	class="token punctuation">}
code>

这时候他就是这样的等价了
c="https://img-blog.csdnimg.cn/56d0276f216a42dabc800aa06d12b1d5.png" alt="在这里插入图片描述" />

2.运算符重载的注意事项

ckquote>

C++为了增强代码的可读性引入了运算符重载c;运算符重载是具有特殊函数名的函数࿰c;也具有其返回值类型࿰c;函数名字以及参数列表࿰c;其返回值类型与参数列表与普通的函数类似。
函数名字为:关键字operator后面接需要重载的运算符符号。
函数原型:返回值类型 operator操作符(参数列表)

ckquote>
  1. 不能通过连接其他符号来创建新的操作符:比如operator@
  2. 重载操作符必须有一个类类型参数
  3. 用于内置类型的运算符࿰c;其含义不能改变࿰c;例如:内置的整型+࿰c;不 能改变其含义
  4. 作为类成员函数重载时࿰c;其形参看起来比操作数数目少1࿰c;因为成员函数的第一个参数为隐藏的this
  5. .* :: sizeof ?: . 注意以上5个运算符不能重载
  6. 操作符是几个操作数࿰c;重载运算符函数就有几个参数

二、赋值运算符重载

1.复制拷贝与拷贝构造

首先我们需要搞清楚复制拷贝与拷贝构造的区别:

  • 拷贝构造是使用一个已经存在的对象去初始化另外一个对象
  • 复制拷贝是已经存在的两个对象进行的复制拷贝
    c="https://img-blog.csdnimg.cn/57286ec540e9451e883c4c7818ad0107.png" alt="在这里插入图片描述" />

2.赋值运算符重载的格式

ckquote>
  • 参数类型:const T&࿰c;传递引用可以提高传参效率
  • 返回值类型:T&࿰c;返回引用可以提高返回的效率࿰c;有返回值目的是为了支持连续赋值
  • 检测是否自己给自己赋值
  • 返回*this :要复合连续赋值的含义
ckquote>

3.赋值运算符重载的实现

<code class="prism language-cpp">	Date class="token keyword">operatorclass="token operator">=class="token punctuation">(class="token keyword">const Dateclass="token operator">& xclass="token punctuation">)
	class="token punctuation">{
		_year class="token operator">= xclass="token punctuation">._yearclass="token punctuation">;
		_month class="token operator">= xclass="token punctuation">._monthclass="token punctuation">;
		_day class="token operator">= xclass="token punctuation">._dayclass="token punctuation">;
		class="token keyword">return class="token operator">*class="token keyword">thisclass="token punctuation">;
	class="token punctuation">}
code>

如上是复制运算符重载的实现࿰c;这里我们需要注意的是࿰c;我们需要传一个返回值࿰c;这是为了满足连续赋值的场景。
但是还需要注意的是࿰c;我们这里的是传值返回࿰c;传值返回的过程会产生一个临时变量࿰c;这个临时变量就会调用拷贝构造函数去构造这个临时变量。然后在将临时变量的数据交给变量
c="https://img-blog.csdnimg.cn/ddb53176d6c64ef2af919e8df184bb7c.png" alt="在这里插入图片描述" />c="https://img-blog.csdnimg.cn/302a244bd85b442f89ba63b88b6a8fc8.png" alt="在这里插入图片描述" />c="https://img-blog.csdnimg.cn/516cf7c3bf2b4818a5c6200c1a17bd0e.png" alt="在这里插入图片描述" />

但是这样的话代价太大了࿰c;因为需要调用拷贝构造࿰c;我们注意到*this出了这个赋值运算符重载函数还在࿰c;所以我们可以使用传引用返回࿰c;这样就减少了拷贝构造的代价了

<code class="prism language-cpp">	Dateclass="token operator">& class="token keyword">operatorclass="token operator">=class="token punctuation">(class="token keyword">const Dateclass="token operator">& xclass="token punctuation">)
	class="token punctuation">{
		cout class="token operator"><< class="token string">"Date operator=(const Date& x)" class="token operator"><< endlclass="token punctuation">;
		_year class="token operator">= xclass="token punctuation">._yearclass="token punctuation">;
		_month class="token operator">= xclass="token punctuation">._monthclass="token punctuation">;
		_day class="token operator">= xclass="token punctuation">._dayclass="token punctuation">;
		class="token keyword">return class="token operator">*class="token keyword">thisclass="token punctuation">;
	class="token punctuation">}
code>

c="https://img-blog.csdnimg.cn/375501c085244b92a0965a97f2c5839b.png" alt="在这里插入图片描述" />有时候࿰c;为了防止我们写出的d1=d1这样的代码࿰c;我们可以进行这样的优化

<code class="prism language-cpp">	Dateclass="token operator">& class="token keyword">operatorclass="token operator">=class="token punctuation">(class="token keyword">const Dateclass="token operator">& xclass="token punctuation">)
	class="token punctuation">{
		cout class="token operator"><< class="token string">"Date operator=(const Date& x)" class="token operator"><< endlclass="token punctuation">;
		class="token keyword">if class="token punctuation">(class="token keyword">this class="token operator">!= class="token operator">&xclass="token punctuation">)
		class="token punctuation">{
			_year class="token operator">= xclass="token punctuation">._yearclass="token punctuation">;
			_month class="token operator">= xclass="token punctuation">._monthclass="token punctuation">;
			_day class="token operator">= xclass="token punctuation">._dayclass="token punctuation">;
		class="token punctuation">}
		class="token keyword">return class="token operator">*class="token keyword">thisclass="token punctuation">;
	class="token punctuation">}
code>

或许可能会认为这里有点作用࿰c;但不大࿰c;只是起了优化的作用而已࿰c;其实不是这样的࿰c;这是因为日期类只涉及到浅拷贝࿰c;如果涉及到深拷贝的话࿰c;就会出现内存泄漏了。所以是非常有必要这样写的

4.赋值运算符重载的注意事项

  1. 赋值运算符只能重载成类的成员函数不能重载成全局函数

因为他是默认成员函数࿰c;自己在类中不写࿰c;编译器会自己生成࿰c;就会产生冲突

  1. 用户没有显式实现时࿰c;编译器会生成一个默认赋值运算符重载c;以值的方式逐字节拷贝。注意:内置类型成员变量是直接赋值的࿰c;而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值

默认生成的赋值运算符重载与拷贝构造的行为一样:

  • 内置类型成员 --值拷贝/浅拷贝
  • 自定义类型成员调用它的赋值重载

所以我们就可以看出来࿰c;Date类和MyQueue类是不需要自己写赋值重载的࿰c;而Stack是需要写的࿰c;因为默认生成的是浅拷贝


本节内容就到这里了࿰c;如果对你有帮助的话࿰c;不要忘记点赞加收藏哦!!!


http://www.niftyadmin.cn/n/331055.html

相关文章

SSH远程终端神器,你在用哪一款

唠嗑部分 在我们日常开发中啊&#xff0c;不可避免的要与Linux打交道&#xff0c;虽然我们作为开发&#xff0c;不要求我们对Linux有多么的专业&#xff0c;但是基本的操作还是要会的&#xff0c;举几个常用的例子&#xff1a; 1、查看nginx配置&#xff0c;配置转发 2、清理…

微信小程序配置实现中英文国际化语言切换

目录 1,在根目录新建文件夹和js文件 2. 在main.js中设置全局语言状态&#xff08;默认设置为中文&#xff09; 3. 页面添加 语言切换按钮&#xff08;登录页面&#xff09; 4. 在需要显示的页面导入使用 1,在根目录新建文件夹和js文件 根目录新建一个locales.js文件, 通用…

第四十三天学习记录:工作相关,更新软件当数据库需要改动时的部分操作

因为欠缺对数据库这一部分的系统学习&#xff0c;导致目前在更新软件涉及到变更数据库时&#xff0c;都要让售后工程师重置软件&#xff0c;以前的数据库数据若想保留极其不方便。虽然数据库也被列为我自学课程列表内&#xff0c;但眼下C语言都还没学习完成。因此&#xff0c;只…

程序员都喜欢怎么使用爬虫?

作为计算机行业的从业人员&#xff0c;程序员常常需要使用各种工具来辅助其工作。其中&#xff0c;爬虫广泛应用于数据收集和采集任务&#xff0c;因此程序员通常喜欢使用爬虫作为快速数据获取工具。本文将讨论程序员为何喜欢使用爬虫、在哪些情况下使用爬虫以及如何使用爬虫进…

【算法学习系列】02 - 你真的有好好使用过 Math.random() 函数吗?

文章目录 说明验证函数等概率返回功能验证 [0, 8)上也是等概率返回一个数的功能验证等概率返回[0, K - 1]中的一个整数实现&#xff1a;任意x&#xff0c;x属于[0, 1)&#xff0c;[0, x)范围上的数出现概率由原来的x调整成x平方 说明 获取随机数大家应该都有用到过 Math.random…

ChitGPT用Laravel写一个答题系统

好的&#xff0c;下面是一个简单的答题系统的Laravel代码。 首先&#xff0c;我们需要创建一个Question模型和一个Answer模型&#xff0c;用于存储问题和答案的数据。我们可以使用以下命令来创建这两个模型&#xff1a; 复制php artisan make:model Question php artisan mak…

nacos注册中心源码分析一之服务注册、服务心跳

源码分析 nacos客户端注册分析 依赖包 <dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency>Nacos的客户端是基于SpringBoot的自动装配实现的 看下依…

NXP MCUXPresso - 操作整理

文章目录 NXP MCUXPresso - 操作整理概念如何在工程中全局搜索文本?在一个编译配置中, 如何排除一些不要的内容?END NXP MCUXPresso - 操作整理 概念 在尝试迁移 openpnp - Smoothieware project 从gcc命令行 MRI调试方式 到NXP MCUXpresso工程. 这个IDE还是蛮喜欢的, 细节…