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://xiahunao.cn/news/3031948.html

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

相关文章

nginx_01

1.安装 yum install epel-release -y # 安装yum的扩展包 yum install nginx -y systemctl start nginx.service #启动nginx systemctl enable nginx.service # netstat -lntup # 查看端口占用情况 # 可以看到nginx默认占用了80端口 2.nginx配置 # 注意配置文件的语法格式…

Django项目之电商购物商城 -- 修改/删除收货地址/设置默认地址

Django项目之电商购物商城 – 修改/删除收货地址/设置默认地址 修改和删除收货地址依旧实在user应用下进行 , 其思路和新增收货地址非常相似 依旧是更具前端的数据来写 在这里修改和删除地址的URL是相同的 , 所以我们只要设置一个模型类就可以实现这两个功能 一 . 修改地址…

了解 条码工具 Dynamsoft 在条码读取器中的形态运算

在图像处理中&#xff0c;术语形态学是指分析形状以填充小孔、去除噪声、提取轮廓等的一组操作。形态学操作很像空间卷积中的过滤过程。有两个部分在起作用&#xff1a;结构元素和预定义的计算规则。 点击下载Dynamsoft最新版https://www.evget.com/product/3691/download 结…

每天五分钟计算机视觉:使用极大值抑制来寻找最优的目标检测对象

本文重点 在目标检测领域,当模型预测出多个候选框(bounding boxes)时,我们需要一种方法来确定哪些候选框最有可能表示真实的目标。由于模型的不完美性和图像中目标的重叠性,往往会有多个候选框对应于同一个目标。此时,极大值抑制(Non-Maximum Suppression,NMS)技术就…

关于docker network网络

首先,我们来看看Docker默认的网络模式,即docker0网桥。 每当你安装Docker时,它会创建一个名为docker0的虚拟网桥,并设置一个IP地址范围供它进行端口映射等工作。所有Docker容器在创建时,都会自动连接到这个docker0网桥,并分配一个虚拟IP地址。这样,容器与主机之间,以及容器与容…

【JavaEE精炼宝库】多线程1(认识线程 | 创建线程 | Thread 类)

目录 一、认识线程 1.1 线程的概念&#xff1a; 1.2 为什么需要线程&#xff1a; 1.3 面试题.谈谈进程和线程的区别&#xff1a; 1.4 Java的线程和操作系统线程的关系&#xff1a; 二、创建线程 2.1 创建线程的5种写法&#xff1a; 2.1.1 写法1.继承 Thread 类&#xf…

简单的表单初始密码验证的实现

目录 简单示例&#xff1a;表单初始密码验证 1.1准备工作(图1&#xff09; 1.2 index部分 1.3 css部分 1.3.1先把css部分链接到index.html中&#xff0c;注意链接的地址。 1.3.2添加样式 1.4 JS部分 1.4.1 先把js部分链接到index.html中&am…

大华智能物联综合管理平台 fastjson远程代码执行漏洞复现

0x01 产品简介 大华ICC智能物联综合管理平台对技术组件进行模块化和松耦合,将解决方案分层分级,提高面向智慧物联的数据接入与生态合作能力。 0x02 漏洞概述 由于大华智能物联综合管理平台使用了存在漏洞的FastJson组件,未经身份验证的攻击者可利用 /evo-runs/v1.0/auths/…

块元素、内联元素、行内块元素

一、介绍&#xff1a; CSS元素划分成块元素、行内元素&#xff08;内联元素&#xff09;、行内块元素等多种常用类型。也就是说&#xff1a;在CSS中&#xff0c;元素根据其在页面上的布局方式被分为不同的显示类型。 背景&#xff1a;HTML负责定义网页的结构和内容&#xff0c…

如何利用AI实现文档处理自动化

文件处理在许多企业及员工看来是一项必不可少、却又耗时费力的工作。每天&#xff0c;他们往往需要花费无数个小时去对文件进行分类、归档、以及搜索。不过&#xff0c;如今人工智能&#xff08;AI&#xff09;正在以自动化的方式改变着这些琐碎的工作。 通过利用人工智能&…

H5 云商城 file.php 文件上传致RCE漏洞复现

0x01 产品简介 H5 云商城是一个基于 H5 技术的电子商务平台,旨在为用户提供方便快捷的在线购物体验。多平台适配:H5 云商城采用 H5 技术开发,具有良好的跨平台适配性。无论是在电脑、手机还是平板等设备上,用户都可以通过网页浏览器访问和使用云商城,无需安装额外的应用程…

在做题中学习(56):二维前缀和模板

【模板】二维前缀和_牛客题霸_牛客网 (nowcoder.com) 理解题意&#xff1a; 要求的是(x1,y1) - (x2,y2)这段区间的和。 解法&#xff1a;二维前缀和 1. 和一维前缀和一样&#xff0c;需要有一个同等规模的dp数组&#xff0c;用来保存一段连续区域的和。 在二维dp中&#xff0…

uni-app(三):离线打包与插件引用(Android)

离线打包与插件引用 1.下载Android离线SDK2.使用Android Studio打开离线打包项目并更新Gradle3.解决报错4.构建5.配置AppKeya.查看证书b.申请AppKeyc.配置AppKey 6.生成本地打包App资源7.拷贝App资源到Android项目中8.修改 appid9.修改Android项目配置文件10.下载证书并配置11.…

完美解决Windows10下-更换JDK环境变量后,在cmd下执行仍java -version然出现原来版本的JDK的问题

一、错误场景预演 本人欲将 JDK 1.8 通过安装包的方式升级为 JDK 22。 本地旧版本&#xff1a;1.8.0_221预升级版本&#xff1a;22.0.1 1.1、查看本地旧版本 在配置环境变量之前&#xff0c;首先我们要明确&#xff0c;本地存在旧版本&#xff0c;如果本地没有 Java&#x…

雷森托尔环保科技有限公司见证2024杭州数字供应链装备展潮流

参展企业介绍 青岛雷森托尔环保科技有限公司创建于2018年&#xff0c;位于山东青岛&#xff0c;现注册资本3000万。公司主营生产模压木托盘、化工木托盘、大型设备木包装、出口木托盘、酒柜木酒架等&#xff0c;公司拥有技术人员6人&#xff0c;均为包装设计专业毕业&#xff0…

云南区块链商户平台:抓包技术自制开票工具(二)

前言 上节我们分析了云南区块链商户平台的登录接口以及数据加密、解密&#xff0c;本节我们将构建一个项目框架&#xff0c;将大致的雏形制作出来 说明 由于我们使用开票软件都是在 云南区块链商户平台上操作&#xff0c;如果再开发电脑端就显得没必要&#xff0c;思考良久&…

架构每日一学 4:成为首席架构师,你必须学会顺应人性

本文首发于公众平台&#xff1a;腐烂的橘子 架构师生存法则之二&#xff1a;架构活动需要顺应人性 程序员入行的第一天起就进入了一个机器的世界。在别人的眼中&#xff0c;程序员平时很少说话&#xff0c;更多的时间在和电脑打交道。 程序员工作时间久了大脑会被格式化&…

HTML5 + CSS3实现卖茶女与水果男的巅峰微信聊天对决,看完后笑一整天

记得之前看过一段卖茶女与水果男的聊天视频&#xff0c;当时觉得真有意思&#xff0c;竟然还可以这样热爱自己的事业。我就想&#xff0c;用HTML5 CSS3实现一下这个过程&#xff0c;锻炼了技术&#xff0c;也娱乐了开发人员&#xff0c;多有意思的一件事啊。 目录 1 实现思路…

Android Studio连接MySQL8.0

【序言】 移动平台这个课程要做一个app的课设&#xff0c;我打算后期增加功能改成毕设&#xff0c;就想要使用MySQL来作为数据库&#xff0c;相对于SQLlite来说&#xff0c;我更熟悉MySQL一点。 【遇到的问题】 一直无法连接上数据库&#xff0c;开始的时候查了很多资料&#…

【通义千问系列】Qwen-Agent 从入门到精通【持续更新中……】

目录 前言一、快速开始1-1、介绍1-2、安装1-3、开发你自己的Agent 二、Qwen-Agent的使用和开发过程2-1、Agent2-1-1、Agent使用2-1-2、Agent开发 2-2、Tool2-2-1、工具使用2-2-2、工具开发 2-3、LLM2-3-1、LLM使用2-3-2、LLM开发 三、基于Qwen-Agent的案例分析3-1、3-2、 总结 …