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\0if ((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. 以下运算符只能通过成员函数进行重载:

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

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

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

相关文章

Excel-VBA报错01-解决方法

【已删除的部件:部件/xl/vbaProject.bin。(Visual Basic for Applications(VBA))】 1.问题复现&#xff1a; Win10 &#xff1b;64位 &#xff1b;Office Excel 2016 打开带有宏的Excel文件&#xff0c;报错&#xff1a;【已删除的部件&#xff1a;部件/xl/vbaProject.bin。…

KBU1010-ASEMI新能源专用KBU1010

编辑&#xff1a;ll KBU1010-ASEMI新能源专用KBU1010 型号&#xff1a;KBU1010 品牌&#xff1a;ASEMI 封装&#xff1a;KBU-4 最大重复峰值反向电压&#xff1a;1000V 最大正向平均整流电流(Vdss)&#xff1a;10A 功率(Pd)&#xff1a;中小功率 芯片个数&#xff1a;4…

抖店选品都怎么选品?什么样的产品更吸引人,更具有购买力?

大家好&#xff0c;我是电商花花。 抖店选品一直都是我们无货源商家的核心问题&#xff0c;不管是出单、还是爆单&#xff0c;店铺想要有销量的前提下都是选品。 很多人一上来就是就是选品&#xff0c;没有选品经验还瞎选品&#xff0c;结果到最后选了一堆出单的产品&#xf…

【栈】Leetcode 验证栈序列

题目讲解 946. 验证栈序列 算法讲解 在这里就只需要模拟一下这个栈的出栈顺序即可&#xff1a;使用一个stack&#xff0c;每次让pushed里面的元素入栈&#xff0c;如果当前栈顶的元素等于poped容器中的当前元素&#xff0c;因此就需要让栈顶元素出栈&#xff0c;poped的遍历…

一篇文章帮你搞定微软云计算证书Renew

IT证书都有过期的时间&#xff0c;像AWS是3年有效期&#xff0c;谷歌是2年&#xff0c;微软是1年&#xff0c;那这些证书到期该怎么Renew更新呢&#xff1f; 小李哥最近的微软AZ-204证书要到期了&#xff0c;到期前半年就会收到Microsoft发来提醒邮件。大家在这半年内任何时间都…

Unity 性能优化之LOD技术(十)

提示&#xff1a;仅供参考&#xff0c;有误之处&#xff0c;麻烦大佬指出&#xff0c;不胜感激&#xff01; 文章目录 LOD技术效果一、LOD技术是什么&#xff1f;二、LODGroup组件介绍三、LODGroup组件使用步骤添加组件添加模型 四、Project Settings中与LOD组件相关参数总结 L…

网络编程套接字 (二)---udosocket

本专栏内容为&#xff1a;Linux学习专栏&#xff0c;分为系统和网络两部分。 通过本专栏的深入学习&#xff0c;你可以了解并掌握Linux。 &#x1f493;博主csdn个人主页&#xff1a;小小unicorn ⏩专栏分类&#xff1a;网络 &#x1f69a;代码仓库&#xff1a;小小unicorn的代…

C++学习第二十九课:C++ 输入输出流详解:从基础到高级应用

在 C 中&#xff0c;流&#xff08;stream&#xff09;是一种用于实现输入输出操作的抽象概念。流可以看作是字节的流动&#xff0c;这些字节可以从一个地方流向另一个地方&#xff0c;例如从键盘输入到程序中&#xff0c;或者从程序输出到屏幕。C 提供了一套完整的流库来处理各…

蓝桥杯13届JAVA A组 国赛

​​​​​​​ package 蓝桥杯国赛; // 贪心选个数最少的进行摆 // 2:1 ,3:1, 4:1,5 : 3,6:3,7:1 // 选 1&#xff0c;7&#xff0c;4&#xff0c;2&#xff0c;3&#xff0c;5&#xff0c;9 // 然后都选满10个 public class 火彩棒数字 {public static void main(String[] a…

【CTFHub】HTTP 请求方式 302跳转 cookie WP

1.请求方式 打开给出的URL进入一个页面&#xff0c;提示原方法是GET&#xff0c;用CTFHUB方法就能获得flag 思路&#xff1a;抓包&#xff0c;将GET方法改成CTFHUB方法进行重新发送请求&#xff0c;查看响应情况 1.打开代理服务器 2.打开BurpSuite 刷新页面获得拦截 3.发送…

网络Http和Socket通讯(基于面试)

每日一句&#xff1a;想得到世上最好的东西&#xff0c;先得让世界看到最好的你 目录 面试问OSI或TCP/IP,怎么回答&#xff1f; 面试问HTTP&#xff1f; 面试问Get类型&#xff0c;Pot类型区别&#xff1f; 面试什么是Socket套接字&#xff1f; 面试问什么是数据粘包&…

qt for android 的架构原理

qt for android实现架构&#xff0c;分享这几幅很不错图。来自于 《Qt 林斌&#xff1a;整合Android IVI平台打造统一的Qt数字座舱体验》 1.实现架构图 2.qt for android能力 3.java 和 qt混合开发 4. AutoMotive

46寸硅片为什么要留平边(flat)?

知 识星球&#xff08;星球名&#xff1a; 芯片制造与封测社区&#xff0c;星球号&#xff1a; 63559049&#xff09;里的学员问&#xff1a; 2&#xff0c; 4&#xff0c; 6寸硅片都有 平 边&#xff0c;它们有什么作用 &#xff1f; 硅片的平边&#xff08;Flat&…

经典权限五张表案例分析

文章目录 模块分析模块分析 描述五张表的关系重要知识讲解抽取成一个BaseServletSpringIOC思想(底层)实现代码IOC概述 SPI机制(为学习框架做思想和技术铺垫)SPI引入1. 标准/规范2. 具体的实现3. 调用 SPI介绍SPI练习JDBC4.0免注册驱动原理Servlet实现方式三 ServletContainerIn…

idea使用前的全局配置,一次配置,多次使用

前提&#xff1a;每次导入一个新的项目&#xff0c;就需要重新设置编码、maven、jdk、git版本等信息。实际每个项目所用到的配置信息是一致的&#xff0c;除非换一家公司&#xff0c;不然不会改动到这些内容。 idea版本&#xff1a;2024.1.1 1.1、全局Maven配置 IDEA启动页面…

微火一文盘点:为何全域运营系统会成为创业新风口?

当前&#xff0c;微火全域运营已经成为创业的新风口&#xff0c;想要做微火全域运营服务商的创业者数量日益增多。据目前了解到的最新消息&#xff0c;微火全域运营系统的市场占有率已经超过了48%&#xff0c;并且还在持续不断地上涨中。 所谓微火全域运营系统&#xff0c;就是…

InLine Chat功能优化对标Github Copilot,CodeGeeX带来更高效、更直观的编程体验!

VSCode中的CodeGeeX 插件上线InLine Chat功能后&#xff0c;收到不少用户的反馈&#xff0c;大家对行内交互编程这一功能非常感兴趣。近期我们针对这个功能再次进行了深度优化&#xff0c;今天详细介绍已经在VSCode插件v2.8.0版本上线的 CodeGeeX InLine Chat功能&#xff0c;以…

蓝桥杯成绩已出

蓝桥杯的成绩早就已经出来了&#xff0c;虽然没有十分惊艳 &#xff0c;但是对于最终的结果我是心满意足的&#xff0c;感谢各位的陪伴&#xff0c;关于蓝桥杯的刷题笔记我已经坚持更新了49篇&#xff0c;但是现在即将会告别一段落&#xff0c;人生即将进入下一个规划。我们一起…

Spring框架学习笔记(一):Spring基本介绍(包含IOC容器底层结构)

1 官方资料 1.1 官网 https://spring.io/ 1.2 进入 Spring5 下拉 projects, 进入 Spring Framework 进入 Spring5 的 github 1.3 在maven项目中导入依赖 <dependencies><!--加入spring开发的基本包--><dependency><groupId>org.springframework<…

基于STC12C5A60S2系列1T 8051单片机实现一主单片机与一从单片机进行双向串口通信功能

基于STC12C5A60S2系列1T 8051单片机实现一主单片机与一从单片机进行双向串口通信功能 STC12C5A60S2系列1T 8051单片机管脚图STC12C5A60S2系列1T 8051单片机串口通信介绍STC12C5A60S2系列1T 8051单片机串口通信的结构基于STC12C5A60S2系列1T 8051单片机串口通信的特殊功能寄存器…