探索C++赋值运算符重载的内部机制:手把手教你精通

news2024/12/27 11:03:11


W...Y的主页 😊

代码仓库分享💕 


🍔前言:

前一篇博客中我们已经了解并学习了初始化和清理模块中的构造函数与析构函数,还有拷贝复制中的拷贝复制函数,它们都是类与对象中重要的成员,今天我们要来讲一下拷贝复制模块中另一个非常重要的内容——赋值重载,但是在学习赋值重载的同时我们应该先学习运算符重载,话不多说我们直接开始!

目录

赋值运算符重载

运算符重载

 赋值运算符重载


赋值运算符重载

运算符重载

C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其
返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	// Date(const Date& d)  // 正确写法
	void print()
	{
		cout << _year << '/' << _month << '/' << _day << endl;
	}
	Date(const Date& d)  // 错误写法:编译报错,会引发无穷递归
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1;
	Date d2(2022,10,23);
	int x = 1; 
    int y = 2;
    bool ret = x > y;
	return 0;
}

 上图是比较的反汇编代码,我们可以看出编译器可以直接做出运算符判断。

一般情况下,我们去比较内置类型都非常容易去比较,因为内置类型都是一些基本变量int、char……我们可以直接使用运算符 ==、<、>等等去比较。但是自定义类型我们应该如何处理呢?

一般情况下我们可以自己创建一个比较函数进行比较:

//判断前面日期是否比后面大
//大返回true,小返回false
bool Greater(Date x, Date y)
{
	if (x._year > y._year)
	{
		return true;
	}
	else if (x._year == y._year && x._month > y._month)
	{
		return true;
	}
	else if (x._year == y._year && x._month == y._month && x._day > y._day)
	{
		return true;
	}

	return false;
}

将此函数放入到类中才可以进行私有数据访问,要不就将基本数据公有化!

上述函数可以轻松解决比大小的问题,但是还是有许多缺陷。比如:函数名的不规范就会导致我们不理解函数的作用。所以对于内置类型的比较C++针对起名字做出了一下规定:

函数名字为:关键字operator后面接需要重载的运算符符号。
函数原型:返回值类型 operator操作符(参数列表)

注意:
不能通过连接其他符号来创建新的操作符:比如operator@
重载操作符必须有一个类类型参数
用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不能改变其含义
作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐
藏的this

.* :: sizeof ?: . 注意以上5个运算符不能重载。这个经常在笔试选择题中出现。 

那针对以上函数我们就可以这样修改:

bool operator>(const Date& x, const Date& y)
{
	if (x._year > y._year)
	{
		return true;
	}
	else if (x._year == y._year && x._month > y._month)
	{
		return true;
	}
	else if (x._year == y._year && x._month == y._month && x._day > y._day)
	{
		return true;
	}

	return false;
}

在参数选择时,我们选择引用而不选择直接传参,这样可以节约时间效率,不用调用拷贝构造函数。 

 这样做我们用户一眼就可以看出此函数的作用,是用来比大小的函数。但是这样去调用函数时函数名还是太长了,而且不直白不美观,所以我们在使用时可以使用非常简单的方式进行调用:

bool ret1 = d1 > d2;

只需要一个运算符就可以调用此函数,是不是非常方便呢!编译器可以帮助我们将这句话转变为,调用函数的语句。

注意:运算符重载与函数重载中都有重载二字,但是绝对是没有任何关系的,函数重载是可以允许参数不同的同名函数,运算符重载是自定义类型可以直接使用运算符。不是姓“张”的都有血缘关系。

但是当我们万事俱备之后进行编译却还是出现报错: 这是为什么呢?

为什么会多一个参数呢?哪里多了?成员参数都会有一个隐藏的参数this指针。所以参数不匹配导致程序出问题。相当于我们在调用函数时使用了两个参数,而设定的参数中却有三个!

我们应该怎么解决问题呢?删一个即可。

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	// Date(const Date& d)  // 正确写法
	void print()
	{
		cout << _year << '/' << _month << '/' << _day << endl;
	}
	//Date(const Date& d)  // 错误写法:编译报错,会引发无穷递归
	//{
	//	_year = d._year;
	//	_month = d._month;
	//	_day = d._day;
	//}
	bool operator>(const Date& y)
	{
		if (_year > y._year)
		{
			return true;
		}
		else if (_year == y._year && _month > y._month)
		{
			return true;
		}
		else if (_year == y._year && _month == y._month && _day > y._day)
		{
			return true;
		}

		return false;
	}

private:
	int _year;
	int _month;
	int _day;
};
//判断日期是否相等
//bool Greater(Date x, Date y)
//bool Compare1(Date x, Date y)


int main()
{
	Date d1;
	Date d2(2022,10,23);
	/*d1 == d2;
	d1 > d2;*/
	bool ret1 = d1 > d2;
	int x = 1, y = 2;
	bool ret = x > y;
	return 0;
}

 我们将参数删除一个即可,编译器看就会转换成d1.operator(d2)在进行转换成d1.operator(&d1,d2)即可,地址就是this指针指向的内容。

我们学明白了运算符重载,那么在进行赋值运算符重载就非常easy了。

 赋值运算符重载

赋值运算符重载格式
参数类型:const T&,传递引用可以提高传参效率
返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值
检测是否自己给自己赋值
返回*this :要复合连续赋值的含义

class Date
{
public :
Date(int year = 1900, int month = 1, int day = 1)
 {
    _year = year;
    _month = month;
    _day = day;
 }
Date (const Date& d)
 {
    _year = d._year;
    _month = d._month;
    _day = d._day;
 }
Date& operator=(const Date& d)
{
if(this != &d)
   {
      _year = d._year;
      _month = d._month;
      _day = d._day;
   }
   
    return *this;
}
private:
    int _year ;
    int _month ;
    int _day ;
};

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

class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
int _year;
int _month;
int _day;
};
// 赋值运算符重载成全局函数,注意重载成全局函数时没有this指针了,需要给两个参数
Date& operator=(Date& left, const Date& right)
{
if (&left != &right)
{
left._year = right._year;
left._month = right._month;
left._day = right._day;
}
return left;
}
// 编译失败:
// error C2801: “operator =”必须是非静态成员

原因:赋值运算符如果不显式实现,编译器会生成一个默认的。此时用户再在类外自己实现
一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值
运算符重载只能是类的成员函数。 用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。注
意:内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符
重载完成赋值

class Time
{
public:
Time()
{
_hour = 1;
_minute = 1;
_second = 1;
}
Time& operator=(const Time& t)
{
if (this != &t)
{
_hour = t._hour;
_minute = t._minute;
_second = t._second;
}
return *this;
}
private:
int _hour;
int _minute;
int _second;
};
class Date
{
private:
// 基本类型(内置类型)
int _year = 1970;
int _month = 1;
int _day = 1;
// 自定义类型
Time _t;
};
int main()
{
Date d1;
Date d2;
d1 = d2;
return 0;
}

既然编译器生成的默认赋值运算符重载函数已经可以完成字节序的值拷贝了,还需要自己实
现吗?当然像日期类这样的类是没必要的。那么下面的类呢?验证一下试试?

// 这里会发现下面的程序会崩溃掉?这里就需要我们以后讲的深拷贝去解决。
typedef int DataType;
class Stack
{
public:
Stack(size_t capacity = 10)
{
_array = (DataType*)malloc(capacity * sizeof(DataType));
if (nullptr == _array)
{
perror("malloc申请空间失败");
return;
}
_size = 0;
_capacity = capacity;
}
void Push(const DataType& data)
{
// CheckCapacity();
_array[_size] = data;
_size++;
}
~Stack()
{
if (_array)
{
free(_array);
_array = nullptr;
_capacity = 0;
_size = 0;
}
}
private:
DataType *_array;
size_t _size;
size_t _capacity;
};
int main()
{
Stack s1;
s1.Push(1);
s1.Push(2);
s1.Push(3);
s1.Push(4);
Stack s2;
s2 = s1;
return 0;
}

 注意:如果类中未涉及到资源管理,赋值运算符是否实现都可以;一旦涉及到资源管理则必
须要实现。


以上就是赋值运算符重载全部内容!!!感谢大家观看。 

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

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

相关文章

构建实时视频聊天应用:使用WebRTC和Netty的完整指南

构建实时视频聊天应用&#xff1a;使用WebRTC和Netty的完整指南 使用WebRTC和Netty构建实时视频聊天应用准备工作步骤1&#xff1a;创建Netty服务器步骤2&#xff1a;创建WebRTC前端应用步骤3&#xff1a;处理WebRTC连接步骤4&#xff1a;处理Netty服务器端步骤5&#xff1a;运…

光流法动目标检测

目录 前言 一、效果展示 二、光流法介绍 三、代码展示 总结 前言 动目标检测是计算机视觉领域的一个热门研究方向。传统的方法主要基于背景建模&#xff0c;但这些方法对于光照变化、遮挡和噪声敏感。因此&#xff0c;研究人员一直在寻找更加鲁棒和有效的技术来解决这一问题。…

如何性能测试中进行业务验证?

在性能测试过程中&#xff0c;验证HTTP code和响应业务code码是比较基础的&#xff0c;但是在一些业务中&#xff0c;这些参数并不能保证接口正常响应了&#xff0c;很可能返回了错误信息&#xff0c;所以这个时候对接口进行业务验证就尤其重要。下面分享一个对某个资源进行业务…

CentOS 7设置固定IP地址

当我们安装了一个虚拟机或者装了一个系统的时候&#xff0c;经常会遇到需要设置固定ip的情况&#xff0c;本文就以Centos 7为例&#xff0c;讲述如何修改固定IP地址。 1、用ifconfig命令查看使用的网卡 如上图所示&#xff0c;我们就会看到我们目前使用的网卡名称 2、编辑网卡…

nginx创建站点“nginx: [emerg] host not found in upstream”错误

nginx配置语法上没有错误的,只是系统无法解析这个域名,所以报错. 解决办法就是添加dns到/etc/resolv.conf 或者是/etc/hosts,让其能够解析到IP。具体步骤如下&#xff1a; vim /etc/hosts 修改hosts文件&#xff0c;在hosts文件里面加上一句 127.0.0.1 localhost.localdomain x…

TiDB x 北京银行丨新一代分布式数据库的探索与实践

北京银行作为中国最大的城商行&#xff0c;坚持以数字化转型统领发展模式、业务结构、客户结构、营运能力、管理方式的五大转型&#xff0c;分布式数据库建设是北京银行数字化转型的重要组成部分。 在新时代、新监管、新业态、新模式的数字化转型背景下&#xff0c;监管要求的…

刚刚腾讯云发布了2023年双11优惠活动!终于等到你

终于等到你&#xff0c;想买台腾讯云服务器&#xff0c;等啊等&#xff0c;终于等来了2023年腾讯云双十一优惠活动&#xff0c;还好没让我失望&#xff0c;2核4G5M带宽的轻量应用服务器三年566&#xff0c;省钱了&#xff1a; txybk.com/go/1111 哈哈哈哈哈。 2023腾讯云双11优…

《低代码指南》——如何用维格表搭建CRM

信息 手机上就能随时随地记录客户信息更智能地进行部门协作、沟通让每一项客户沟通都有迹可循一个表格实现客户全生命周期管理企业如何在激烈的市场竞争中崭露头角,拥有自己的立足之地,CRM 系统必然是一大助力。但传统 CRM 系统功能太多太复杂,不够灵活,内部推广、维护又很…

Linux常用命令——clear命令

在线Linux命令查询工具 clear 清除当前屏幕终端上的任何信息 补充说明 clear命令用于清除当前屏幕终端上的任何信息。 语法 clear实例 直接输入clear命令当前终端上的任何信息就可被清除。 在线Linux命令查询工具

Python下载安装

本文以Windows下安装python3.6为例 一、进入Python的官网&#xff0c;链接: python官网 二、选择下载&#xff0c;选择Windows 三、选择自己需要版本的python进行下载 四、选择所下载的exe文件&#xff0c;选择Upgrade Now 五、等待下载 六、安装成功

基于蝗虫算法的无人机航迹规划-附代码

基于蝗虫算法的无人机航迹规划 文章目录 基于蝗虫算法的无人机航迹规划1.蝗虫搜索算法2.无人机飞行环境建模3.无人机航迹规划建模4.实验结果4.1地图创建4.2 航迹规划 5.参考文献6.Matlab代码 摘要&#xff1a;本文主要介绍利用蝗虫算法来优化无人机航迹规划。 1.蝗虫搜索算法 …

Java实现添加文字水印、图片水印

目录 前言 一、获取原图片对象信息 1、读取本地图片 2、读取网络图片 二、处理水印 三、添加水印 四、获取目标图片 五、完整工具类 六、结果展示 前言 现在很多人都喜欢在各种平台上分享自己的照片吧&#xff0c;不管是一些制作出来的媒体图片还是精致的人像图片&…

人大金仓三大兼容:MySQL迁移无忧

近日&#xff0c;MySQL 5.7停服事件引发广泛关注。MySQL目前已经成为中国用户使用非常广泛的数据库&#xff0c;其中5.7版本的用户比重又是最高的。随着信息技术应用创新深入各行各业&#xff0c;国产数据库对MySQL的平滑替换成为大势所趋。 作为数据库领域国家队&#xff0c;人…

Jmeter并发压测数据库的TPC值

Apache JMeter 视频讲解演示&#xff1a;https://www.bilibili.com/video/BV1Dh4y1J7NW/ Apache组织开发的基于Java的压力测试工具&#xff0c;常常用来模拟高并发压测场景 下载网址&#xff1a;https://jmeter.apache.org/download_jmeter.cgi 下载二进制包&#xff0c;解…

【深度学习 | Transformer】释放注意力的力量:探索深度学习中的 变形金刚,一文带你读通各个模块 —— 总结篇(三)

&#x1f935;‍♂️ 个人主页: AI_magician &#x1f4e1;主页地址&#xff1a; 作者简介&#xff1a;CSDN内容合伙人&#xff0c;全栈领域优质创作者。 &#x1f468;‍&#x1f4bb;景愿&#xff1a;旨在于能和更多的热爱计算机的伙伴一起成长&#xff01;&#xff01;&…

【linux系统】服务器安装Pycharm

文章目录 安装pycharm步骤1. 进入pycharm官网2. 上传到服务器3. 安装过程 摘要&#xff1a;pycharm是Python语言的图形化开发工具。因为如果在Linux环境下的Python shell 中直接进行编程&#xff0c;其无法保存与修改&#xff0c;在大型项目当中这是很不方便的&#xff0c;而py…

【废话文学】各种概念混搭

我认为他一定是在主体意识中出现了一种异常的反馈 这种反馈打破了既定的习惯性模式 于是思维意识出现了层阶梯式的神话 我认为通过XXX同志这个主体意识上的问题 要看出他自身的轨迹而带有意念性 这个悲剧带有鲜明的主观色彩和思辨色彩 而不要只听着在对他人生哲学上的虚无上的研…

自动化测试工具的定义及作用

在现代软件开发中&#xff0c;质量和效率是至关重要的。为了确保软件在不断变化的市场中脱颖而出&#xff0c;开发团队需要寻找方法来提高质量、降低错误率&#xff0c;并加速交付速度。自动化测试工具是一种不可或缺的资源&#xff0c;可以帮助开发团队实现这些目标。本文将深…

前三季净利降八成!科大讯飞增长放缓,刘庆峰怎么应对

大数据产业创新服务媒体 ——聚焦数据 改变商业 国内A股上市公司中&#xff0c;科大讯飞一度是唯一一家连续十年营收增长达到25%的高科技企业。财报显示&#xff0c;在三年疫情最为艰难的2021年&#xff0c;科大讯飞依旧录得183亿元营收&#xff0c;同比增长40%&#xff1b;归…

系统设计 - 我们如何通俗的理解那些技术的运行原理 - 第四部分:微服务架构

本心、输入输出、结果 文章目录 系统设计 - 我们如何通俗的理解那些技术的运行原理 - 第四部分&#xff1a;微服务架构前言典型的微服务架构是什么样的微服务的优势 微服务最佳实践在开发微服务时&#xff0c;我们需要遵循以下最佳实践&#xff1a; 微服务通常使用什么技术堆栈…