掘根宝典之C++运算符重载

什么是运算符重载

运算符重载是一种形式的C++多态。这很容易让大家想到函数多态,让我们使用同名的函数来完成相同的基本操作,即使是用于不同的类型,不懂函数多态的看这里http://t.csdnimg.cn/ipbqR

运算符重载把重载的概念拓展到运算符上,允许赋予C++运算符多种含义。

实际上C++已经重载了很多运算符了,比如"*“,可以用于乘法,也可以用于解引用

C++根据操作数的数目和类型来决定采用哪种操作

例如啊,我们知道数组是不能相加的啊,那我们可以通过重载运算符来实现数组“相加”,赋予特定意义

实现下面这种操作

int a[10];
int b[10];
int c=a+b;

实现运算符重载

重载运算符的本质是编写以运算符为名称的函数

格式为

返回类型  operator运算符 (参数列表)
{
函数体
}

先看个例子

#include<iostream>
using namespace std;
class AA
{
private:int a_;
public:AA(int a){a_ = a;}
//重载运算符int operator+(AA& t){return a_ + t.a_;}
};
int main()
{AA e = { 2 };AA r = { 3 };
//检查运算符结果cout << e + r << endl;
}

发现打印出来的是5,这说明我们重载后的+也可以用于对象相加了

重载运算符的规则

重载运算符有以下限制:

1. 重载运算符只能作用于自定义类型的对象,不能用于内置类型(如int、float等),也就是说重载之后的运算符的操作数必须有一个是自定义类型。

2.C++中不允许用户定义新的运算符,只能对已有的运算符进行重载,

3.重载后的运算符的优先级、结合性也应该保持不变,也不能改变其操作个数和语 法结构。

4.重载后的含义,与操作基本数据类型的运算含义应类似,如加法运算符“+”,重 载后也应完成数据的相加操作。

5.有5个运算符不可重载:类关系运算符“:”、成员指针运算符“*”、作用域运算 符“::”、sizeof运算符、三目运算符“?:”

6.运算符重载函数不能有默认参数,否则就改变了运算符操作数的个数,是错误的。

7.运算符重载函数既可以作为类的成员函数,也可以作为类的友元函数,也可以是全局函数。 

8.作为全局函数时,一般都需要在类中将该函数声明为友元函数。因为该函数大部分情况下都需要使用类的private成员。

9.作为类的成员函数时,二元运算符的参数只有一个, 一元运算符不需要参数(参数没有任何意义,只是为了区分是前置还是后置形式:带 一个整型参数为后置形式)。因为有个参数(左操作数)是隐含的(隐式访问this指针 所指向的当前对象)。

10.作为全局函数或者友元函数时,二元操作符需要两个参数,一元操作符需要一 个参数,而且其中必须有一个参数是对象,好让编译器区分这是程序员自定义的运算符, 防止程序员修改用于内置类型的运算符的性质。

重载运算符的几种情况

1.重载为普通全局函数

重载运算符可以是普通函数,但是有个限制,就是它的函数参数必须有一个类类型或枚举类型

看个例子 

#include<iostream>
using namespace std;
class AA
{public:     //注意是publicint a;int b;
};
int operator+(AA t,AA y)//可以
{return t.a + y.a;
}
int operator+(AA t,int y)//可以
{return t.a + y;
}int operator+(int t,int y)//这是不行的
{return t+ y;
}int main()
{AA f = { 1,2 };AA g = { 3,4 };cout << f + g << endl;cout<<  f+3<<endl;
}

但是上面的AA类的数据成员是public的(如果是private的话,普通全局函数没权限访问),这违反了类的原则之一——数据隐藏,所以我们很少重载为普通的全局函数 

2.重载为类的成员函数

重载运算符最常见的就是在类里面作为类方法进行重载的


(1),双目运算符


如果是双目运算符重载为类的成员函数,则它有两个操作数:左操作数是对象本身 的数据,由this指针指出(我们不写),右操作数则通过运算符重载函数的参数表来传递(必须要写)

也就是说,我们只需写下右操作数即可

定义格式:

返回类型 operator操作符(右操作数类型)
{
函数体
}

调用格式为:

左操作数 操作符 右操作数 

编译器会自动将上面这个语句展开为下面这种 

左操作数.operator运算符(右操作数)

当然我们也可以直接使用第二种 

 看个例子

#include <iostream>
using namespace std;class A
{
private:int x, y;
public:A(int x1 = 0, int y1 = 0) //默认构造函数{x = x1;y = y1;}A operator+(const A& a)const   //const成员函数,运算符重载{A t;t.x = this->x + a.x;t.y = this->y + a.y;return t;}void show(){cout << "x=" << x << "," << "y=" << y << endl;}
};
int main()
{A a1(1, 2);A a2(3, 4);A a;a = a1 + a2;//实际上等价于a=a1.operator+(a2);a.show();
}

输出结果

x=4,y=6

我们这也是成功重载运算符+


(2),单目运算符


如果是单目运算符重载为类的成员函数,则要分为前置(++i)和后置(i++)运算符。

如果是前置运算符,则它的操作数是函数调用者,函数没有参数。

前置调用格式为:

操作符 操作数;

被编译器展开为 

操作数.operator运算符()

后置调用格式为 

操作数 操作符;

被编译器展开为 

操作数.operator运算符(操作数类型)

举个例子 

#include <iostream>
using namespace std;class A
{
private:int x, y;
public:A(int x1 = 0, int y1 = 0){x = x1;y = y1;}A &operator++()//++i 前置++实现{++x;    //先自增++y;return *this;    //后引用}A operator++(int)    //i++ 后置++实现{//int参数没有任何意义,只是为了区分是前置还是后置形式A a = *this;    //保存对象引用++(*this);    //自增,调用前面实现的前置++return a;    //返回先前保存的对象}void show(){cout << "x=" << x << "," << "y=" << y << endl;}
};
int main()
{A a1(1, 2), a2(3, 4);(a1++).show();(++a2).show();return 0;
}

输出结果

x=1,y=2x=4,y=5


实现前置“++”时,数据成员进行自增运算,然后返回当前对象(即this指针所指 向的对象)。实现后置“++”时,创建了一个临时对象来保存当前对象的值,然后再将当前对象自增,最后返回的是保存了初始值的临时对象。

注意:前置单目运算符和后置单目运算符的最主要区别是函数的形参,后置单目运 算符带一个int型形参,但它只起区分作用。

3.重载为类的友元函数


运算符重载为类的友元函数,只是在函数前加一个friend关键字。

(1)重载格式

friend 返回类型 operator 运算符(参数列表){函数体;}

明:运算符重载为类的友元函数时,由于没有隐含的this指针,因此,操作数的个数没有变化,所有的操作数都必须通过函数形参进行传递,函数的参数与操作数 自左自右保持一一对应。

调用格式为:

左操作数 操作符 右操作数 

编译器会自动将上面这个语句展开为下面这种 

operator 运算符(左操作数,右操作数);

例如:调用a1+a2相当于函数调用operator+(a1, a2)

下面看一个例子:

将”+”运算符重载为类的友元函数

#include <iostream>
using namespace std;class A
{
private:int x, y;
public:A(int x1 = 0, int y1 = 0){x = x1;y = y1;}friend A operator+(const A& a, const A& b);void show(){cout << "x=" << x << "," << "y=" << y << endl;}
};
A operator+(const A& a, const A& b)
{return A(a.x + b.x, a.y + b.y);
}
int main()
{A a1(1, 2), a2(3, 4);A c;c = a1 + a2;//可以替换为c=operator+(a1,a2);c.show();return 0;
}

输出结果

x=4,y=6


4.两种重载方式的选择(不包括普通全局函数)


多数情况下,将运算符重载为类的成员函数和类的友元函数都是可以的,但两者各具特点:

1,一般,单目运算符最好重载为类的成员函数,双目运算符最好重载为类的友元函数。

2,若一个运算符的操作需要修改对象的状态,选择重载为成员函数较好。

3,若运算符的操作数(尤其是第一个操作数)可能有隐式类型转换,则只能选用友元函 数。

4,具有对称性的运算符可能转换任意一端的运算对象,如:算术(a+b和b+a)、关系运算 符(a>b和b<a)等,通常重载为友元函数。

5,有4个运算符必须重载为类的成员函数:赋值=、下标[ ]、调用( )、成员访问->。

重载

常用运算符的重载

1.输入输出运算符的重载


C++标准库对左移运算符<<和右移运算符>>分别进行了重载,使其能够用于不同数 据的输入输出。对于基本类型数据(如bool、int、double等)和标准库所包含的 类(如string、complex、ofstream、ifstream等)可以直接使用输入运算符“>>”、 输出运算符“<<”进行读写操作。对于自己定义的新数据类型(如:类对象)则需要重载这两个运算符。

cout<<obj1 <<endl;  cin>>obj1;   //cout是ostream类的对象,cin是istream类的 对象

由于输入输出操作的第一个操作数为ostream/istream对象,也就是说左侧的运算 对象必须是ostream/istream对象,如果重载成类的成员函数,则左侧的操作对 象将是我们定义的一个类对象(需要修改标准库中的类,显然不是我们所期望 的)。因此输入输出运算符不可重载为类的成员函数,只能重载为非成员函数 (全局函数,友元函数)。

重载“<<”和“>>”运算符的一般格式为:

istream& operator>>(istream&, 类对象引用);         //输入运算符>>重载ostream& operator<<(ostream&, const 类对象引用);    //输出运算符<<重载

说明
(1)对于输入运算符来说,第一个参数是istream对象的引用,第二个参数是要 向其中存入数据的对象,不能为常量返回istream类对象的引用(可作为下次调 用时的第一个参数),是为了能够连续读取:cin>>c1>>c2;,让代码书写更加漂亮。 如果不返回引用,就只能一个一个地读取:cin>>c1;  cin>>c2;

cin>> c; 可以理解为:operator >> (cin , c);

(2)对于输出运算符“<<”来说,第一个参数是ostream对象引用,因为向流中 写入数据会改变流的状态 ,所以不能用const修饰ostream对象。由于采用了引用 的方式进行参数传递,并且也返回了对象的引用,所以重载后的运算符可以实现连 续输出。

cout<< c; 可以理解为:operator<< (cout , c);

案例

#include <iostream>
using namespace std;class A
{
private:int x, y;
public:A(int x1 = 0, int y1 = 0){x = x1;y = y1;}friend istream& operator>>(istream& is, A& a);friend ostream& operator<<(ostream& os, const A& a);
};
istream& operator>>(istream& is, A& a)
{is >> a.x >> a.y;return is;
}
ostream& operator<<(ostream& os, const A& a)
{os << "x=" << a.x << "," << "y="<<a.y ;return os;
}
int main()
{A a1(1, 2);cin >> a1;cout << a1;return 0;
}

输出结果

21 88x=21,y=88


重载输入输出运算符后,便可像基本数据类型一样直接对类对象进行输入输出,不 用再编写用于输入输出对象数据成员的show()等成员函数,而且使用起来更为简洁。

注意:
通常情况下,输出运算符主要负责输出对象的内容,而非控制格式,此时不应该把 换行符也重载进去。如果把换行符也重载进去,用户有时想在同一行输出一些描述 性的文本,就无法完成了。因此,在重载输出运算符时,应尽量少一些格式化操作, 可以使用户控制更多的输出细节。

2,关系运算符的重载


关系运算符(如“==”或“<”)也是较常用的一类运算符,同样,若类对象间需要 完成比较操作,也应完成对关系运算符的重载。关系运算符重载的过程非常直观。 重载关系运算符一般都返回true或false值。

关系运算符共有六个,并且相互之间有对应关系,如小于运算符“<”与大于运算 符“>”对应,因此对于关系运算符的重载应成对完成。

可重载为类的成员函数也可重载为类的友元函数(具有对称性的运算符通常重载为 类的友元函数)

案例

#include <iostream>
using namespace std;class A
{
private:int x, y;
public:A(int x1 = 0, int y1 = 0){x = x1;y = y1;}friend bool operator>(const A& a1,const A& a2);friend bool operator<(const A& a1,const A& a2);friend bool operator==(const A& a1, const A& a2);
};
bool operator>(const A& a1, const A& a2)
{return a1.x > a2.x;
}
bool operator<(const A& a1, const A& a2)
{return a1.x < a2.x;
}
bool operator==(const A& a1, const A& a2)
{return a1.y == a2.y;
}
int main()
{A a1(1, 2);A a2(3, 2);if (a1 > a2){cout << "a1的x大于a2的x"<<endl;}else if (a1 < a2){cout << "a1的x小于a2的x" << endl;}if (a1 == a2){cout << "a1的y都等于a2的y" << endl;}return 0;
}

输出结果

a1的x小于a2的xa1的y都等于a2的y


重载了”>”,”<”,”==”的关系运算符,其他的也是一样,重载关系运算符之后可以直 接比较对象的大小,实际上”>”,”<”只是比较了对象中的x值,”==”只是比较了对象 中的y值,

重载关系运算符的准则
(1)关系运算符都要成对的重载。如:重载了“<”运算符,就要重载“>” 运算符,反之亦然。

(2)“==”运算符应该具有传递性。如:a==b,b==c,则a==c成立。

(3)当成对出现运算符重载时,可以把一个运算符的工作委托给另一个运算 符。如:重载“!=”运算符是在“==”运算符的基础上,重载“<”运算 符 由重载过的“>”运算符来实现。

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

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

相关文章

EMC学习笔记(二十五)降低EMI的PCB设计指南(五)

线缆和连接器 1 差模和共模噪声2 串扰3 返回路径数量4 外部PCB -IO 布局建议5 防止噪音和静电放电 tips&#xff1a;资料主要来自网络&#xff0c;仅供学习使用。 设计良好的两层板&#xff0c;和大多数四层板&#xff0c;有最小的辐射。系统级的问题是由于将PCB与任何板外支持…

vector容器

1. vector基本概念 1.1 功能&#xff1a; vector数据结构和数组非常相似&#xff0c;也称为单端数组 vector与普通数组区别&#xff1a; 不同之处在于数组是静态空间&#xff0c;而vector可以动态扩展 动态扩展&#xff1a; 并不是在原空间之后续接新空间&#xff0c;而是找更…

视频讲解:优化柱状图

你好&#xff0c;我是郭震 AI数据可视化 第三集&#xff1a;美化柱状图&#xff0c;完整视频如下所示&#xff1a; 美化后效果前后对比&#xff0c;前&#xff1a; 后&#xff1a; 附完整案例源码&#xff1a; util.py文件 import platformdef get_os():os_name platform.syst…

方舟基金:若美机构按最大夏普率配置比特币,则有望将其推升至230-250万美元...

号外&#xff1a;教链内参2.12《方舟基金重磅报告〈大胆想象2024〉全文pdf》 方舟基金&#xff08;Ark Invest&#xff09;的木头姐&#xff08;Cathie Wood&#xff09;是业内的老熟人了。她一向以大胆的预测而著称。比如就在2023年10月份&#xff0c;木头姐在采访中就曾直言&…

​​​​​​​C#系列-C#EF框架的优缺点+针对大数据处理的优化(19)

C#EF框架的优缺点 C# EF&#xff08;Entity Framework&#xff09;框架的优缺点如下&#xff1a; 优点&#xff1a; 简单易用&#xff1a;EF框架提供了丰富的API和工具&#xff0c;使得开发者可以轻松地实现数据库的增删改查等操作&#xff0c;无需编写繁琐的SQL语句。对象化…

MIT-BEVFusion系列七--量化2_Camera、Fuser、Decoder网络的量化

目录 Camera 量化Camera Backbone (Resnet50) 量化替换量化层&#xff0c;增加residual_quantizer&#xff0c;修改bottleneck的前向对 Add 操作进行量化 Camera Neck (GeneralizedLSSFPN) 量化将 Conv2d 模块替换为 QuantConv2d 模块Camera Neck 中添加对拼接操作的量化替换 C…

STM32自学☞对射式红外传感器计数

infrared_count.c文件 /* 编写步骤 一、初始化函数 1.开启GPIO、AFIO时钟 (NVIC和EXIT不需要开启&#xff0c;因为EXIT时钟一直处于开启状态&#xff0c;而NVIC是内核里的外设和CPU处在一起且RCC管理的是内核外的外设&#xff0c;综上所述&#xff0c;所以不用开启) 2.配…

【机器学习】卷积和反向传播

一、说明 自从 AlexNet 在 2012 年赢得 ImageNet 竞赛以来&#xff0c;卷积神经网络 (CNN) 就变得无处不在。从不起眼的 LeNet 到 ResNets 再到 DenseNets&#xff0c;CNN 无处不在。 您是否想知道 CNN 的反向传播中会发生什么&#xff0c;特别是反向传播在 CNN 中的工作原理。…

Stream流学习笔记

Stream流 创建流中间操作1、filter2、map3、distinct4、sorted5、limit6、skip7、flatMap 终结操作1、forEach2、count3、max&min4、collect5、查找与匹配 创建流 单例集合&#xff1a;集合对象.stream() List<Integer> list new ArrayList<>(); Stream<…

uniapp禁止截屏录屏/投屏功能(adb投放失效)

经过测试代码写在App.vue会失效,请写在符合逻辑的界面 uni-app 实现安卓防截屏 在uniapp中实现 要在App中全局禁止截屏&#xff0c;那么可以在App.vue中调用 如果想要某个页面防截屏就在某个页面调用&#xff0c;但是在离开这个页面的时候要恢复截屏&#xff0c;否则全局…

C语言:详解操作符(下)

上一篇链接&#xff1a;C语言&#xff1a;详解操作符&#xff08;上&#xff09;摘要&#xff1a; 在上篇文章中&#xff0c;我们已经讲过位操作符等涉及二进制的操作符&#xff0c;这些有助于帮助我们后期理解数据如何在计算机中运算并存储&#xff0c;接下来本篇将更多的讲述…

OpenCV学习路线图

下面是针对初学者设计的一个学习路线图&#xff1a; 第1周&#xff1a;计算机视觉和OpenCV概述 介绍计算机视觉及其应用领域讨论OpenCV的历史、特点和工作原理OpenCV的安装和配置&#xff08;Windows/Linux/macOS&#xff09;了解OpenCV的基本数据结构&#xff08;cv::Mat等&…

如何清理Docker占用的磁盘空间?

在Docker中&#xff0c;随着时间的推移&#xff0c;占用的磁盘空间可能会不断增加。为了保持系统的稳定性和性能&#xff0c;定期清理Docker占用的磁盘空间非常重要。下面将介绍一些清理Docker磁盘空间的方法。 一、清理无用的容器 有时候&#xff0c;我们可能会运行一些临时…

mac docker 宿主机和容器间网络打通

动因 是这样&#xff0c;笔者最近满怀欣喜入手Docker&#xff0c;看着各种文章命令都是不断点头称道&#xff1a;“嗯嗯&#xff0c;不错不错”,在接下来终于准备大干一场的时候碰壁了&#xff0c;主要情况是说在Mac中跑了第一把的时候发现碰到&#xff0c;虚拟机和宿主机居然…

Vue-1

Vue 简介 Vue 是一个用于"构建用户界面(基于数据渲染出用户看到的画面)" 的渐进式框架 基本使用 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widt…

SpringBoot 2.7.6 集成Activiti 7.1.0.M6 所需的配置文件

<?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org/POM/4.0.0 https://…

课时25:内容格式化_常用符号解读_信息传递

3.1.1 信息传递 学习目标 这一节&#xff0c;我们从 重定向、管道符、小结 三个方面来学习。 重定向 重定向符号 在shell脚本中有两类常见的重定向符号&#xff1a; 覆盖式重定向&#xff1a;> 表示将符号左侧的内容&#xff0c;以覆盖的方式输入到右侧文件中< 表示…

c# avalonia 实现正方体翻转效果

在Avalonia中要实现一个正方体的翻转效果&#xff0c;需要利用动画和变换的功能&#xff0c;但由于Avalonia主要是2D UI框架&#xff0c;对3D支持有限。你可以通过2D的方式来近似模拟3D翻转的效果&#xff0c;或者配合像Avalonia3D这样的扩展库来实现。 示例代码大纲如下&#…

C#系列-C#访问WebAPI(11)

在C#中访问Web API通常涉及使用HttpClient类来发送HTTP请求到Web服务器并接收响应。以下是一个简单的示例&#xff0c;展示了如何使用HttpClient来调用一个Web API并获取JSON响应。 首先&#xff0c;确保你的项目中包含了System.Net.Http命名空间。如果你使用的是.NET Core或.…

CentOS7下如何安装Nginx

一、Ngxin是什么 Nginx是一个开源的 Web 服务器&#xff0c;具有反向代理、负载均衡、缓存等功能。它可以作为 HTTP 服务器&#xff0c;将服务器上的静态文件&#xff08;如 HTML、图片&#xff09;通过 HTTP 协议展现给客户端&#xff0c;也可以实现动静分离&#xff0c;把动态…