C嘎嘎~~ [类 下篇之运算符重载]

news2024/10/4 18:34:08

类 下篇 之运算符重载

  • 5.赋值运算符重载
    • 5.1运算符重载
      • 5.1.1 运算符的概念
      • 5..1.2 重载运费符的位置
      • 5.1.3运算符重载的实质
    • 5.2 赋值运算符重载
      • 5.2.1深刻理解---编译器生成的默认赋值运算符重载
      • 5.2.2深刻理解---拷贝构造和赋值运算符重载
      • 5.2.3深刻理解---传参和返回值用引用修饰

5.赋值运算符重载

5.1运算符重载

5.1.1 运算符的概念

C语言学习的时候, 我们可以用运算符来操作内置类型. 比如, 比较两个整数的大小, 比较两个整数是否想相等… …
那么我们能不能也用操作符来对自定义类型来进行操作??

class Date
{

public:

	Date(int year = 2023, int month = 5, int day = 5)
	{
		_year = year;
		_month = month;
		_day = day;
	}

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

int main()
{
	Date d1(2023, 5, 6);
	Date d2(2003, 5, 7);

	bool ret = d1 > d2;
}

*****
error C2676: 二进制“>:“Date”不定义该运算符或到预定义运算符可接收的类型的转换
*****

通过上面的代码, 我们可以发现是不行的~~.
其实, 不用代码来作佐证, 我们也可以猜想的出来 <== 因为不知道要比较什么啊. 到底是比较两个对象的年, 还是比较两个对象的月, 还是比较两个对象的日??
由此, 我们可以基本得出 ⇒ 内置类型可以直接使用操作符, 而自定义类型只能自己来写~~(当然, 这个有一个是意外的, 就是后面要讲的 赋值运算符重载)

为了解决自定义类型的 操作符使用, 引入了operator 这个关键字. operator + 操作符 ⇒ 就会导致该操作符重载

C++为了增强代码 的可读性引入了运算符重载, 运算符重载是具有特殊函数名的函数.

  • 函数的一般形式: 返回值类型 + operator + 操作符 (参数列表)
  • 函数是可以具有返回值类型的, 函数名, 以及参数列表

注意点:

  1. 不能创造新的操作符 , 比如 operator @
  2. 操作数至少有一个自定义类型 ⇐ 因为操作符重载就是用于自定义类型的, 如果都是内置类型, 我们就不用且不能改变, 编译器已经自己完成.
  3. 作为成员函数时, 形参比原本的操作数少一个⇐ 因为作为成员函数, 第一个形参就已经确定了, 就是this指针, 然而this指针是不在参数列表中显示的
  4. 名字虽然是重载, 但是这个重载却跟函数重载的重载不是一个意思. 这个重载的意思是: 让用户更加直观地知道这个运算符是等同功能作用于自定义类型的.
  5. 运算符重载函数是可以构成重载的
  6. 有五个操作符是不可以进行运算符重载的, .*(这个是 点乘, 不知道有啥用)) ::(作用域限定域) sizeof(计算变量 或 类型的大小) ?:(三目运算符) .(访问符)

5…1.2 重载运费符的位置

一般来说, 重载运费符函数是可以放在类中充当成员函数的, 也可以直接放在全局变量中的

  1. 重载运费符在全局变量
class Date
{

public:
    
    // 构造函数
	Date(int year = 2023, int month = 5, int day = 5)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	
//private:
	int _year;
	int _month;
	int _day;
};

bool operator== (const Date& x1, const Date& x2)
{
	if (x1._year == x2._year && x1._month == x2._month && x1._day == x2._day)
		return true;

	return false;
}

int main()
{
	Date d1(2023, 5, 6);
	Date d2(2003, 5, 7);

	bool ret = d1 == d2;
	cout << ret << endl;

}

*****
0
*****
  1. 重载运算符在类中
class Date
{

public:

	bool operator==(const Date& x)
	{
		if (_year == x._year && _month == x._month && _day == x._day)
			return true;

		return false;
	}

	// 构造函数
	Date(int year = 2023, int month = 5, int day = 5)
	{
		_year = year;
		_month = month;
		_day = day;
	}


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


int main()
{
	Date d1(2023, 5, 6);
	Date d2(2003, 5, 7);

	bool ret = d1 == d2;
	cout << ret << endl;

}

*****
0
*****

通过上面两组代码的对比, 我们可以发现:

  1. 相比较写在全局变量中, 充当成员函数, 我们就用少写一个操作数
  2. 写在全局变量中, 我们就不能用对象去访问类中的私有变量(我把私有放开了~)
  3. 其实, 用对象去访问私用变量有两种方式: 一是充当成员函数, 二是用友元函数(这个建议能不用就不用, 会破坏封装的)

5.1.3运算符重载的实质

我们已经知道的是用内置类型使用操作符是将 例如 " a > b"这样的操作符抓换成指令.
那么, 我们使用运算符重载的实质又是什么??

前面, 我们已经知道运算符重载是用函数来实现的, 我们就大胆猜想⇒ 使用运算符重载的实质就是调用函数


通过上面的佐证, 我们发现了 使用运算符重载 和 调用函数 的指令是一样的 ⇒ 使用运算符重载的实质就是调用函数


5.2 赋值运算符重载

有些老铁就有疑问: 我们之前的默认成员函数不是还没有讲完嘛, 怎么又讲这个运算符重载??
在日常的应用中, 赋值是非常常见的 ⇒ 所以赋值运算符是一个默认成员函数
运算符重载就是 为下面的赋值运算符重载打基础的~~

赋值运算符重载的格式:

  • 参数类型: const 类名&, 传递引用可以提高效率
  • 返回值类型: 为了支持连续赋值, 返回类类型; 建议返回类型传引用, 也是提高效率
  • 返回 *this: 也是为了支持连续赋值
  • 也要注意是否自己给自己赋值
  • 普通的运算符重载的位置可以是在全局变量中的, 但是赋值运算符重载只能充当成员函数⇐ 因为它是默认成员函数, 如果写在外面 会和编译器生成的默认赋值运算符重载构成歧义.

5.2.1深刻理解—编译器生成的默认赋值运算符重载

编译器默认生成的运算符重载的行为 跟 编译器默认生成的默认拷贝构造函数是一样的
对内置类型: 按内存字节去拷贝, 相当于memcpy, 是浅拷贝, 也叫值拷贝
对自定义类型: 会调用自己的拷贝构造函数

处理:
对于没有资源申请的类型: 可以使用编译器默认生成的赋值运算符重载
对于有资源申请的类型: 自己来写一个赋值运算符重载函数

5.2.2深刻理解—拷贝构造和赋值运算符重载

拷贝构造: 用一个对象去初始化另一个对象(就是在对象实例化的时候, 直接初始化)
赋值运算符重载: 已经存在的,两个对象的复制

class Date
{
public:
	// 赋值运算符重载
	Date operator=(const Date& x)
	{
		if (this != &x)
		{
			_year = x._year;
			_month = x._month;
			_day = x._day;
		}

		return *this;
	}

	Date(int year = 2023, int month = 5, int day = 5)
	{
		_year = year;
		_month = month;
		_day = day;
	}

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

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

int main()
{
	// 构造
	Date d1(2023,5,9);
	Date d4;
	d1.Print(); // 2023 5 9
	d4.Print(); // 2023 5 5 

	// 拷贝构造
	Date d2(d1);
	Date d3 = d1;
	d2.Print(); // 2023 5 9
	d3.Print(); // 2023 5 9

	// 赋值运算符重载
	d1 = d4;
	d1.Print(); // 2023 5 5
	d4.Print(); // 2023 5 5

}

有人就会有疑问: d3 = d1 为什么是拷贝构造啊? 明明用的就是赋值啊?
抓重点: 拷贝构造就是初始化对象啊, 赋值就是两个已存在的对象之间的操作~~


5.2.3深刻理解—传参和返回值用引用修饰

class Date
{
public:
	// 赋值运算符重载
	Date& operator=(const Date& x)
	{
		if (this != &x)
		{
			_year = x._year;
			_month = x._month;
			_day = x._day;
		}

		return *this;
	}

	Date(int year = 2023, int month = 5, int day = 5)
	{
		_year = year;
		_month = month;
		_day = day;
	}

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

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

int main()
{
	Date d1(2023, 5, 6);
	Date d2(2003, 5, 7);
	Date d3;

	d1 = d2 = d3;
	d1.Print();
	d2.Print();
	d3.Print();
}

*****
2023 5 5
2023 5 5 
2023 5 5

*****
  • 传参用引用: 相信大家应该还记得 自定义类型传值传参和赋值是会自动调用拷贝构造的, 如果我们不写拷贝构造函数, 将会使用浅拷贝.⇒ 所以自定义类型传参的时候就要传递引用, 由于是赋值, 传递的对象是不会发生变化的, 建议用const修饰一下
  • 返回值用引用: 如果我们返回的是一个引用的话, 就不会生成一个临时拷贝, 那么也就不会调用拷贝构造(浅拷贝看的不是很明显, 深拷贝的代价更高)

写到这里, 有些小伙伴应该会问: 这里为什么可以返回 *this啊, 不是出了函数就已经销毁了吗? ⇒ 那么这样就不能返回引用啊??


誓言是世界上最靠不住的东西,只有你对别人还有用的时候,别人才会遵守誓言。

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

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

相关文章

CUDA error: device-side assert triggered CUDA kernel errors might be asynchronously reported at some

问题描述&#xff1a; 在修改代码时&#xff0c;出现入下报错。 发生异常: RuntimeError CUDA error: device-side assert triggered CUDA kernel errors might be asynchronously reported at some other API call,so the stacktrace below might be incorrect. For debuggi…

Android Switch开关按钮使用和自定义样式(系列教程五)

Switch开关按钮简介 Switch开关按钮是Android中的基本控件之一&#xff0c;其本质上也是一个按钮&#xff0c;具有开和关两种展示状态。 Switch开关按钮基本使用 在布局文件中定义开关按钮&#xff1a; <LinearLayoutandroid:layout_width"300dp"android:layo…

Vivado安装后添加器件库

1.前言 通常安装Vivado时&#xff0c;由于软件完整安装的空间需求过于庞大&#xff0c;一般只会选择一部分器件进行安装。而随着学习和工作的进展&#xff0c;遇到新的赛灵思朋友是成长的里程碑&#xff0c;也是综合不能通过的绊脚石。 今天有幸认识了一位新的赛灵思朋友——…

云原生: istio+dapr构建多运行时服务网格

2020 年&#xff0c;Bilgin Ibryam 提出了 Multi-Runtime&#xff08;多运行时&#xff09;的理念&#xff0c;对基于 Sidecar 模式的各种产品形态进行了实践总结和理论升华。那到底什么是多运行时呢&#xff1f;首先还是得从分布式应用的四大类基本需求讲起。简单来讲任何分布…

刷题练习3

文章目录 题目一分析题解 题目二分析第一种第二种 题解第一种方法代码第二种方法代码 题目一 题目链接 描述 读入一个字符串str&#xff0c;输出字符串str中的连续最长的数字串 输入描述&#xff1a; 个测试输入包含1个测试用例&#xff0c;一个字符串str&#xff0c;长度不超…

牛顿迭代法解超越方程

牛顿迭代法解超越方程 L g T 2 2 π t a n h ( 2 π L d ) L\frac{gT^2}{2\pi}tanh(\frac{2\pi}{L}d) L2πgT2​tanh(L2π​d) 方程&#xff1a; f ( L ) L − g T 2 2 π t a n h ( 2 π L d ) 0 f(L)L-\frac{gT^2}{2\pi}tanh(\frac{2\pi}{L}d)0 f(L)L−2πgT2​tanh(L2π…

~项目启动~

rmq是什么&#xff1f; "rmq" 可能指的是 "RabbitMQ"&#xff0c;它是一种开源消息队列软件&#xff0c;采用 AMQP&#xff08;Advanced Message Queuing Protocol&#xff09;协议&#xff0c;可以用于支持异步处理、任务分发、解耦合等应用场景。Rabbit…

仪表检测与读数(一):仪表检测

基于YOLOv4的仪表检测 前言YOLOv4源码下载数据集处理与模型训练模型性能测试 前言 本系列是想记录一下自己实现的一种用于仪表检测与读数的方法&#xff0c;首先方法仅针对于单指针仪表和单行显示的数字仪表进行了检测与读数方法的设计。方法的整体思路是&#xff1a;第一步对拍…

[ 云计算 | Azure ] Chapter 06 | 计算服务之虚拟机、虚拟机规模集、Azure 容器、Azure App 与 Azure Functions

本系列已经更新文章列表&#xff08;已更新&#xff09;&#xff1a; [ Azure 云计算从业者 ] Chapter 03 | 描述云计算运营中的 CapEx 与 OpEx&#xff0c;如何区分 CapEx 与 OpEx[ Azure 云计算从业者 ] Chapter 04 | Azure核心体系结构组件之数据中心、区域与区域对、可用区…

【Qt】插件Plugin入门之Q_PLUGIN_METADATA()宏【2023.05.07】

摘要 分析Q_PLUGIN_METADATA宏的设计意图&#xff0c;站在设计者的意图进行插件的高屋建瓴式学习。 Meta-Object Compiler 简称MOC Qt 的 Meta-Object Compiler&#xff08;MOC&#xff09;是一个预处理器&#xff0c;用于处理带有特殊关键字的 C 文件&#xff0c;并生成用于…

Linux命令·netstat

netstat命令用于显示与IP、TCP、UDP和ICMP协议相关的统计数据&#xff0c;一般用于检验本机各端口的网络连接情况。netstat是在内核中访问网络及相关信息的程序&#xff0c;它能提供TCP连接&#xff0c;TCP和UDP监听&#xff0c;进程内存管理的相关报告。 如果你的计算机有时候…

详细版简单易学版TypeScript各类型声明

假如本地新建了一个b.ts文件 安装TypeScript&#xff1a;npm install -g typescript 编译代码&#xff1a;tsc b.ts 运行js&#xff1a;node b.js 在终端输入 tsc -init 生成 tsconfig.json 文件 类型注解&#xff1a;TypeScript里的类型注解是一种轻量级的为函数或变量添加约束…

Python中模块和包基础学习

目录 模块 引入模块 使用from...import语句引入模块中的指定变量或函数 使用import...as语句给模块起别名 使用dir()函数查看模块中的所有变量和函数 使用__name__变量判断模块是被导入还是直接执行 包 注意 示例 模块 Python中的模块是指一个文件&#xff0c;可以包…

2.1 掌握NumPy数组对象ndarray

2.1 掌握NumPy数组对象ndarray 2.2.1 创建数组对象1&#xff0e;数组创建2&#xff0e;数组属性&#xff1a;ndarray&#xff08;数组&#xff09;是存储单一数据类型的多维数组。3&#xff0e;数组数据类型 2.1.2 生成随机数random模块常用随机数生成函数 2.1.3 通过索引访问数…

Python中异常处理的学习

目录 异常的基本介绍 异常处理语句 抛出异常 异常的基本介绍 在Python中&#xff0c;如果程序出现错误&#xff0c;会抛出异常。异常是一种Python对象&#xff0c;它封装了错误的信息&#xff0c;并提供了一种处理错误的机制。Python中内置了很多异常类型&#xff0c;包括但…

C语言-学习之路-07

C语言-学习之路-07 内存管理作用域局部变量静态&#xff08;static&#xff09;局部变量全局变量extern全局变量声明全局函数和静态函数 内存分布内存分区 内存管理 作用域 C语言中变量的作用域可分为&#xff1a;代码作用域、函数作用域、文件作用域 局部变量 局部变量也叫…

C嘎嘎~~ [类 下篇(2)]

类 下篇2 5.赋值运算符重载5.1运算符重载5.1.1 运算符的概念5..1.2 重载运费符的位置5.1.3运算符重载的实质 5.2 赋值运算符重载5.2.1深刻理解---编译器生成的默认赋值运算符重载5.2.2深刻理解---拷贝构造和赋值运算符重载5.2.3深刻理解---传参和返回值用引用修饰 5.赋值运算符…

ADAS-透视前方:汽车HUD技术原理解析

“ 当人们谈论未来的汽车技术时&#xff0c;汽车HUD&#xff08;Head-Up Display&#xff09;是一个经常被提及的技术。HUD是一种驾驶辅助技术&#xff0c;它可以将关键的驾驶信息直接显示在驾驶员的视线范围内&#xff0c;让驾驶员无需转移视线就能获得所需信息。这个技术在过…

HZNUCTF2023 web

目录 <1> guessguessguess <2> ezflask(无过滤ssti) ​ <3> ppppop(cookie泄露反序列化) <4> ezlogin(bool盲注) <5> ezpickle(pickle反序列化) <6> eznode(原型链污染) <1> guessguessguess 尝试是否存在sql注入&#xff0c;…

03- 目标检测数据集和标注工具介绍 (目标检测)

要点&#xff1a; 常用数据集和标注工具 标注工具 PPOCRLabel github地址&#xff1a;paddleocrlabel 参考文档&#xff1a;目标检测简介 - 知乎 一 目标检测数据集 1. PASCAL VOC VOC数据集是目标检测经常用的一个数据集&#xff0c;自2005年起每年举办一次比赛&#xff…