C++运算符重载(操作符重载)

运算符重载

  • 1. 运算符重载基础
    • 1.1 运算符重载语法
    • 1.2 运算符重载细节补充
    • 1.3 更多的运算符重载
  • 2. 重载单目运算符
  • 3. 如何直接输入输出对象类型——重载运算符 << 和 >>
    • 3.1 单个对象实现 cou <<
    • 3.2 多个对象实现 cout<<
    • 3.3 右移运算符 输入 cin >>
    • 3.4 重载括号运算符(仿函数/函数对象)
  • 4. 重载运算符注意事项

1. 运算符重载基础

C++将运算符重载扩展到自定义的数据类型,它可以让对象操作更美观。

例如字符串string用加号(+)拼接、cout用两个左尖括号(<<)输出。

有时候我们需要让对象之间进行运算, C++ 提供的“运算符重载”机制,赋予运算符新的功能,就能解决用+将两个复数对象相加这样的问题。

  • 举个例子,比如说如下代码:
    类Point
class Point {
	friend Point add(Point, Point);
	int m_x;
	int m_y;
public:
	Point(int x, int y) :m_x(x), m_y(y) {}
	void display() {
		cout << "(" << m_x << ", " << m_y << ")" << endl;
	}

};

函数add()

Point add(Point p1, Point p2) {
	return Point(p1.m_x + p2.m_x, p1.m_y + p2.m_y);
}

然后在主函数里面我们定义两个对象,让两个点相加

int main() {
	Point p1(10, 20);
	Point p2(20, 30);
	

	Point p3 = add(p1, p2);
	p3.display();

	getchar();
	return 0;
}

输出:
在这里插入图片描述
这样做就显得很繁琐,如果可以直接这么写就好了:

Point p3 = p1 + p2;

也就是直接让这两个对象进行相加操作,
但是默认情况下这么写报错
所以我们可以利用 运算符重载(操作符重载):可以为运算符增加一些新的功能

1.1 运算符重载语法

返回值 operator运算符(参数列表);

接着刚才的问题,我们利用运算符重载为运算符增加一些新的功能,使得“+”可以允许两个Point类型进行相加操作。

Point operator+(Point p1, Point p2) {
	return Point(p1.m_x + p2.m_x, p1.m_y + p2.m_y);
}

不要忘记在Point类里面更新友元:

friend Point operator+(Point, Point);

完整代码:

#include <iostream>
using namespace std;

class Point {
	friend Point operator+(Point, Point);
	int m_x;
	int m_y;
public:
	Point(int x, int y) :m_x(x), m_y(y) {}
	void display() {
		cout << "(" << m_x << ", " << m_y << ")" << endl;
	}

};

//Point add(Point p1, Point p2) {
//	return Point(p1.m_x + p2.m_x, p1.m_y + p2.m_y);
//}
Point operator+(Point p1, Point p2) {
	return Point(p1.m_x + p2.m_x, p1.m_y + p2.m_y);
}

int main() {
	Point p1(10, 20);
	Point p2(20, 30);
	Point p3 = p1 + p2;
	p3.display();

	getchar();
	return 0;
}

输出:
在这里插入图片描述

  • 本质上是调用了 operator+() 函数,并且返回了 Point 类型值
// 这两行代码等价
Point p3 = operator+(p1, p2);

Point p3 = p1 + p2;
  • 利用运算符重载为运算符后,三个数相加也可以
    例:
int main() {
	Point p1(10, 20);
	Point p2(20, 30);
	Point p3(30, 40);
	Point p4 = p1 + p2 + p3;

	p4.display();

	getchar();
	return 0;
}

输出:
在这里插入图片描述
此时三个对象相加也可以了,所以还是比直接调用 add()函数 要方便的多

原理是这样的:

// 调用了两次operaator+
Point p4 = operator+(operator+(p1, p2), p3) ;

1.2 运算符重载细节补充

之前在 C++ 对象型参数和返回值 的帖子笔记中提到,最好不要在函数参数使用对象型类型,不然会产生中间变量。

Point operator+(Point p1, Point p2) {
	return Point(p1.m_x + p2.m_x, p1.m_y + p2.m_y);
}

修改成 引用(减少中间对象的产生) :

Point operator+(const Point &p1, const Point &p2) {
	return Point(p1.m_x + p2.m_x, p1.m_y + p2.m_y);
}

前面用 const 修饰的原因是可以使加入进来的参数接收对象更广,既可以接受const对象,也可以接受非const对象。

记得更细友元:

	friend Point operator+(const Point &, const Point &);
  • 有没有发现这种写法和拷贝构造函数的格式很类似呢?

拷贝构造函数格式: 类名(const 类名& 对象名){…}

// 拷贝构造函数
	Point(const Point &point) {
		m_x = point.m_x;
		m_y = point.m_y;
	}

我的理解是也是由于 const 引用既可以接受const参数也可以接受非const参数的原因,它的接受范围更大

  • 另外,运算符重载也可以直接写在类里面,这样就可以直接访问类里面的成员,我们就省下了去写友元这一步
  • p.s: const 用来修饰成员函数,如果是在类外,全局函数就不要用const修饰了
#include <iostream>
using namespace std;

class Point {
	friend Point operator+(Point, Point);
	int m_x;
	int m_y;
public:
	Point(int x, int y) :m_x(x), m_y(y) {}
	void display() {
		cout << "(" << m_x << ", " << m_y << ")" << endl;
	}
	// 运算重载符
	const Point operator+(const Point &point){
		return Point(this->m_x + point.m_x, this->m_y + point.m_y);  // this 指针可以省略
	}

};
	

变成成员函数以后,则需要用对象去调用

p1.operator+(p2);
//等价于
p1 + p2;

那么成员函数中只接收一个参数就可以了

// 运算重载符
	Point operator+(const Point &point){
		return Point(this->m_x + point.m_x, this->m_y + point.m_y);  // this 指针可以省略
	}

还可以再完善以下,左边的这个const是防止返回值被赋值

右边的const能保证我们的返回值可以再次调用operator+()函数

// 运算重载符
	const Point operator+(const Point &point)const{
		return Point(this->m_x + point.m_x, this->m_y + point.m_y);  // this 指针可以省略
	}
  • 结论:全局函数和成员函数都支持运算符重载

1.3 更多的运算符重载

除了 “ + ”,还有其他的运算符重载,如 “ - ”(减法运算)

	const Point operator-(const Point &point)const{
		return Point(m_x - point.m_x, m_y - point.m_y);
	}

" += "

	Point &operator+=(const Point &point) {
		m_x += point.m_x;
		m_y += point.m_y;
		return *this;	// 取出this指针所指向的东西
	}

" == "

	bool operator==(const Point &point) const {
		// 1\0
		if ((m_x == point.m_x) && (m_y == point.m_y)) {
			return 1;
		} else {
			return 0;
		}
		// 或者以下写法
		// return (m_x == point.m_x) && (m_y == point.m_y);

“ != ”

	bool operator!=(const Point &point) const {
		return (m_x != point.m_x) || (m_y != point.m_y);
	}

" - " (负号)

	const Point operator-() const {
		return Point(-m_x, -m_y);
	}

2. 重载单目运算符

可重载的一元运算符:

1)++ 自增 2)-- 自减 3)! 逻辑非 4)& 取地址
5)~ 二进制反码 6)* 解引用 7)+ 一元加 8) - 一元求反

一元运算符通常出现在它们所操作的对象的左边。

但是,自增运算符++和自减运算符–有前置和后置之分。

C++ 规定,重载++或–时,如果重载函数有一个int形参,编译器处理后置表达式时将调用这个重载函数。

  • 区别:1.只需要在参数后面加个 int 就是后置++
// 前置++
	Point &operator++() {
		m_x++;
		m_y++;
		return *this;
	}
// 后置++
	const Point operator++(int) { // 返回const是由于 后置++ 是不能被赋值的(运算符后置++本身的特性)
		Point old(m_x, m_y);
		m_x++;
		m_y++;
		return old; // 返回的是临时的变量,因为前置++是先赋值再运算,也就是最后在进行++操作
	}
  • 2.前置++可以被赋值,后置++不可以
int main() {
	int a = 10;
	
	(++a) = 20; // ++放在前面可以赋值
	
	(a++) = 20; // 会报错,因为相当于先赋值,a再自己+1
}
  • 所以在写后置++的重载的时候,我们一个是要考虑它本身的不能被赋值的特性(前面用const修饰),一个是返回值应当返回+1之前的值
// 后置++
	const Point operator++(int) { // 返回const是由于 后置++ 是不能被赋值的(运算符后置++本身的特性)
		Point old(m_x, m_y);
		m_x++;
		m_y++;
		return old; // 返回的是临时的变量,因为前置++是先赋值再运算,也就是最后在进行++操作
	}

3. 如何直接输入输出对象类型——重载运算符 << 和 >>

3.1 单个对象实现 cou <<

  • 我们能否能像输出其他类型一样,直接将对象进行 cout 呢?
    可以通过对左移运算符进行重载
int main() {
	Point p1(10, 20);
	
	cout << p1 << endl; 
	
	getchar();
	return 0;
}

这种情况重载就不能写在类里了,因为左移运算符左边是cout,而想要调用类里的成员函数首先要是对象才行,而cout显然不是对象,所以要写在类的外面

// output stream -> ostream 输出流
void operator<<(ostream& cout, const Point& point) {
	cout << "(" << point.m_x << ", " << point.m_y << ")";
}

(ostream 是 cout 所在的类)

注意不要忘记在类里面放友元,不然无法访问m_x,m_y成员变量

friend void operator<<(ostream &, const Point &);

3.2 多个对象实现 cout<<

  • 如果想实现多个数据的cout:
cout << p1 << p2 << endl;

其实类似如下过程:

cout << p1 << p2 << endl;
// 等价于
operator << (cout, p1) << p2 ;

其实就是得到一次返回值以后,再用这个返回值调用一次左移运算符,所以我们可以更新一下:

// output stream -> ostream 输出流
ostream& operator<<(ostream& cout, const Point& point) {
	cout << "(" << point.m_x << ", " << point.m_y << ")";
	return cout; // 返回cout本身,然后就可以继续打印
}

更新友元:

friend ostream& operator<<(ostream&, const Point&);

可以用以下代码实验

int main() {
	Point p1(10, 20);
	Point p2(20, 30);

	cout << p1 << p2 << endl; 

	getchar();
	return 0;
}

输出:可以正常打印对象了

在这里插入图片描述

3.3 右移运算符 输入 cin >>

  • 如果我们想从键盘输入一些东西给到对象,该怎么做?
Point p1(10,20);
cin >> p1;

先在类里面更新友元:

friend istream& operator>>(istream&, Point&);

然后和cout类似,istream是cin所在的类,返回一个cin

// input stream -> istream
istream &operator>>(istream &cin, Point &point) {
	cin >> point.m_x;
	cin >> point.m_y;
	return cin;
}

最后可以通过键盘输入查看对象中的值是否被修改了,键盘输入:40 50 60 70

int main() {
	Point p1(10, 20);
	Point p2(20, 30);

	cin >> p1 >> p2;
	cout << p1 << p2 << endl; 

	getchar();
	return 0;
}

输出:
在这里插入图片描述
对象里的成员变量的值已经更改为键盘输入的值了

3.4 重载括号运算符(仿函数/函数对象)

括号运算符()也可以重载,对象名可以当成函数来使用(函数对象、仿函数)。

括号运算符重载函数的语法:返回值类型 operator()(参数列表);

注意:

  1. 括号运算符必须以成员函数的形式进行重载。
  2. 括号运算符重载函数具备普通函数全部的特征。
  3. 如果函数对象与全局函数同名,按作用域规则选择调用的函数。

函数对象的用途:

  1. 表面像函数,部分场景中可以代替函数,在STL中得到广泛的应用;
  2. 函数对象本质是类,可以用成员变量存放更多的信息;
  3. 函数对象有自己的数据类型;
  4. 可以提供继承体系。

4. 重载运算符注意事项

  1. 返回自定义数据类型的引用可以让多个运算符表达式串联起来。(不要返回局部变量的引用)
  2. 重载函数参数列表中的顺序决定了操作数的位置。
  3. 重载函数的参数列表中至少有一个是用户自定义的类型,防止程序员为内置数据类型重载运算符。
  4. 如果运算符重载既可以是成员函数也可以是全局函数,应该优先考虑成员函数,这样更符合运算符重载的初衷。
  5. 重载函数不能违背运算符原来的含义和优先级
  6. 不能创建新的运算符。
  7. 以下运算符不可重载:

sizeof sizeof运算符
. 成员运算符
.*    成员指针运算符
::    作用域解析运算符
?:    条件运算符
typeid    一个RTTI运算符
const_cast    强制类型转换运算符
dynamic_cast    强制类型转换运算符
reinterpret_cast   强制类型转换运算符
static_cast    强制类型转换运算符

暂时先写这么多,这部分的知识点还有很多,后面遇到再回来补充

  1. 以下运算符只能通过成员函数进行重载:

= 赋值运算符
() 函数调用运算符
[] 下标运算符
-> 通过指针访问类成员的运算符

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

python输出希腊字母

有时候在绘制一些函数图像时&#xff0c;需要坐标轴和图例显示希腊字母 plt.xlabel(r’ ϵ \epsilon ϵ’)

土壤多参数检测仪在农业生产有哪些优势?

在现代农业的快速发展中&#xff0c;土壤多参数检测仪凭借其独特的功能和优势&#xff0c;为农业生产注入了新的活力。以下是其在农业生产中的几个显著优势&#xff1a; 一、全面精准的检测能力 土壤多参数检测仪能够全面、精准地检测土壤中的多种参数&#xff0c;如pH值、电…

【机器学习】线性回归:以房价预测为例

线性回归&#xff1a;揭秘房价预测的黑科技 一、引言二、线性回归概述三、房价预测实例数据收集与预处理特征选择与建模模型评估与优化 四、总结与展望 一、引言 在数字化时代&#xff0c;数据科学已成为推动社会进步的重要引擎。其中&#xff0c;线性回归作为数据科学中的基础…

Android 面试之Kotlin 协程上下文和异常处理

本文首发于公众号“AntDream”&#xff0c;欢迎微信搜索“AntDream”或扫描文章底部二维码关注&#xff0c;和我一起每天进步一点点 上下文是什么 CoroutineContext是一组用于定义协程行为的元素&#xff0c;包括以下几部分&#xff1a; Job&#xff1a;控制协程的生命周期Co…

一文讲透亚马逊云三层架构

关于三层架构&#xff0c;我们有很多想说的话&#xff1a; &#xff08;以下内容以下都在VPC中&#xff09; cloudfront做CDN加速网关规划S3做静态网站托管APIGateway作为统一网关入口认证/限流Lambda 作为传统后端&#xff0c;并发&#xff0c;底层架构Redis缓存DDB作为持久化…

Excel 根据分类及组内序号进行编码

例题描述和简单分析 Excel 记录课程数据&#xff0c;未排序&#xff0c;部分如下&#xff1a; ABC1CourseDateTime2Word1-Sep-209:003Word1-Sep-209:004PowerPoint1-Sep-209:005Word1-Sep-2012:006PowerPoint1-Sep-2012:007Excel1-Sep-2012:008Word1-Sep-2012:00 现在要新增…

发布一个属于自己的 npm工具包

我们可以发布一个属于自己的工具包到 npm 服务上&#xff0c;方便自己和其他开发者使用&#xff0c;参与社区贡献&#xff0c;操作步骤如下&#xff1a; 创建与发布 npm 初始化工具包&#xff0c;package.json 填写包的信息 (包的名字是唯一的)注册账号 https://www.npmjs.co…

13.跳跃游戏

文章目录 题目简介题目解答解法一&#xff1a;贪心算法&#xff0b;动态规划代码&#xff1a;复杂度分析&#xff1a; 题目链接 大家好&#xff0c;我是晓星航。今天为大家带来的是 跳跃游戏面试题 相关的讲解&#xff01;&#x1f600; 题目简介 题目解答 思路&#xff1a;这…

Django开发实战之定制管理后台界面及知识梳理(中)

上一篇文章末尾讲到如何能够展示更多的字段在界面上&#xff0c;那么针对整个界面数据&#xff0c;如果我想按照某一个条件进行筛选&#xff0c;我该怎么做呢&#xff0c;只需要加上下面一行代码 注意&#xff1a;中途只有代码片段&#xff0c;文末有今天涉及的所有代码 1、增…

RabbitMQ高级(MQ的问题,消息可靠性,死信交换机,惰性队列,MQ集群)【详解】

目录 一、MQ的问题 1. 问题说明 2. 准备代码环境 1 创建project 2 创建生产者模块 3 创建消费者模块 二、消息可靠性 1. 介绍 2. 生产者确认机制 3. MQ消息持久化 4. 消费者确认机制 5. 消费者auto模式的失败重试 6. 小结 三、死信交换机和延迟消息 1. 介绍 2. …

【前端部署】Ubuntu22.04 使用nginx部署vue前端项目教程

一.ubuntu安装nginx 1.更新本地软件包列表 sudo apt update2.安装nginx sudo apt install nginx3.验证nginx是否安装成功 sudo systemctl status nginx如果Nginx正在运行&#xff0c;则命令输出应该显示Active&#xff08;active (running)&#xff09;状态。 4.若nginx未运…

【MySQL】——课程平台的创建设计

&#x1f4bb;博主现有专栏&#xff1a; C51单片机&#xff08;STC89C516&#xff09;&#xff0c;c语言&#xff0c;c&#xff0c;离散数学&#xff0c;算法设计与分析&#xff0c;数据结构&#xff0c;Python&#xff0c;Java基础&#xff0c;MySQL&#xff0c;linux&#xf…

2024HW Linux应急响应基础学习

首先展示关于Linux的关键目录&#xff0c;这是应急响应查看的关键&#xff1a; 常用命令 top //查看进程资源的占用情况 ps -aux //查看进程 直接写ps aux也可以 netstat -antpl //查看网络连接 ls -alh /proc/pid //查看某个pid对应的可执行程序 pid记得修改 lsof /…

微信登录功能--网站应用

微信开发平台注册https://open.weixin.qq.com/ 账号中心-填写基本资料&#xff08;最好是公司注册&#xff09; 账号中心-开发者资质认证&#xff08;充钱&#xff0c;300&#xff09; 审核通过之后&#xff0c;管理中心-网站应用-创建网站应用&#xff08;AppSecret一定一定…

SMART700西门子触摸屏维修6AV6 648-0CC11-3AX0

西门子工控机触摸屏维修系列型号&#xff1a;PС477,PC677,TD200,TD400,KTP178,TP170A,TP170B,TP177A,TP177B,TP270,TP277,TP27,MP370,MP277,OP27,OP177B等。 触摸屏故障有&#xff1a;上电黑屏, 花屏,暗屏,触摸失灵,按键损坏,电源板,高压板故障,液晶,主板坏等,内容错乱、进不了…

nacos server安装部署傻瓜级教程

下载地址&#xff1a;GitHub - alibaba/nacos: an easy-to-use dynamic service discovery, configuration and service management platform for building cloud native applications.an easy-to-use dynamic service discovery, configuration and service management platfo…

C++ 搜索二叉树

目录 1.二叉搜索树概念 2. 实现二叉搜索树 2.1. 二叉搜索树的插入 2.2查找 2.3删除节点 3.二叉树的应用&#xff08;KV结构&#xff09; 1.二叉搜索树概念 二叉搜索树又称二叉排序树&#xff0c;它或者是一棵空树&#xff0c;或者是具有以下性质的二叉树: 若它的左子树不为…

AutoCAD中密集的填充打散后消失的问题

有时候在AutoCAD中&#xff0c;图案填充的填充面积过大或填充太过密集时&#xff0c;将该填充打散&#xff0c;也就是执行Explode时&#xff0c;会发现填充图案消失了。 原因是打散后线条太大&#xff0c;系统就不显示了。可以通过设置&#xff1a;HPMAXLINES 值&#xff0c;来…

奇诡 matlab 小 bug matlab git需要记录的改动太多

似乎是我有一次添加了太多的路径之后的事情。但是不敢说一定是这个导致的&#xff1a; 症状&#xff1a;只要对文本进行任何编辑操作&#xff0c;工作区就会出现"Processing … Cancel"的提示&#xff0c;如果不管的话这个提示不会消失&#xff0c;同时matlab变得越来…

Minimal-Supervised Medical Image Segmentation via Vector Quantization Memory

文章目录 Minimal-Supervised Medical Image Segmentation via Vector Quantization Memory摘要方法实验结果 Minimal-Supervised Medical Image Segmentation via Vector Quantization Memory 摘要 辅助重构分支&#xff1a;该分支通过提供额外的监督并产生学习视觉表示所需…