c++学习之运算符重载详解

news2024/11/26 11:28:00

目录

1.运算符重载的基本概念

2.重载加法运算符

3.重载运算符<<(全局函数实现)

4.重载>>(输入)运算符(全局函数实现)

5. 重载++/--运算符

6.重载==运算符

7.可以重载的运算符


1.运算符重载的基本概念

运算符重载:就是对已有的运算符重新进行定义,赋予一种功能,以适应不同的数据类型。

运算符只能运算内置的数据类型,对于自定义的数据类型,不能运算,所以 我们可以重载运算符

语法:定义重载的运算符就像定义函数,只是函数的名字为operator+重载的符号。比如重载+,函数名就是operator+。

重载思路:

1.弄懂运算符的对象个数(个数决定重载函数时的参数)

2.识别运算符的运算对象 :是类的对象,还是其他

类的对象:全局函数实现(不推荐)         成员函数实现(推荐,少一个参数)可以用this指针

其他:只能是全局函数实现

比如我们在成员函数里重载+,对于ob1+ob2,在调用重载函数时:operator+(ob1,ob2)

对于重载函数的一般调用:会先ob1调用函数,为ob1.operator+(ob2),在ob2调用函数为ob1.operator+(ob2.operator+).

考虑到this指针是指向函数首地址,即ob1调用函数的地址。故我们在设计函数时,利用this指针,可以少写一个参数:

operator+(ob2),直接调用重载+的函数会替换为ob1.operator+(ob2).

2.重载加法运算符

这里我们先已简单的重载+运算符为例:

因为普通的加法只对默认的数据类型操作,我们自定义的类的对象是无法让操作符操作的。需要重载运算符实现某些运算。

比如:我们实现person类的相加是将person类里的age相加。

#define _CRT_SECURE_NO_WARNINGS
 using namespace std;
 class person
 {
 public:
	 person(int age)
  {
	this-> age = age;
  }
   int age;
 };

 person operator+(person &p1, person &p2)
 {
  person p(p1.age+p2.age);
  return p;
 }
void test01()
{
	 person p1(10);
	 person p2(20);
	 person p3 = p1 + p2;// operator+(p1,p2)
	 cout << p3.age << endl;
 }
 int main()
 {
	 test01();//结果为30
	 return 0;
 }

这里我们定义了全局函数的+,重载了+运算符,但程序在对运算符调用时,还是会根据参数类型,调用重载后的运算符,还是调用默认的运算符。

同时我们说了运算符重载对于成员函数来说,我们是可以利用this指针的特性实现参数的优化

例如:

using namespace std;
 class person
 {
 public:
	 person(int age)
 {
	this-> age = age;
 }
		 person operator+(person & p2)
	{
		 person p(this-> age + p2.age);
		 return p;
	}
		 int age;
  };

void test01()
{
	 person p1(10);
	 person p2(20);
	 person p3 = p1 + p2;//    p1.operator+(p2)
	 cout << p3.age << endl;
 }

 我们直接在类里重载+运算符,我们直接p1的this指针调用重载的函数,实现参数的优化。

3.重载运算符<<(全局函数实现)

我们知道默认的cout<<只会输出基本的数据类型,如整形,字符型。想要访问类里的自定义对象,需要重载该运算符。举例:

#include<iostream>
#include<string>
using namespace std;

class person
{
private:
	//friend void operator<<(ostream& out, person& lucy);
    //friend ostream& operator<<(ostream& out, person& lucy);
	int num;
	string name;
	float score;
public:
	person(){}
	person(int num, string name, float score) :num(num), name(name), score(score){}
	void showperson(int num, string name, float score)
	{
		cout << num << endl;
		cout << name << endl;
		cout << score << endl;
	}
};
int main()
{
	person lucy(100, "lucy", 99.5);
	cout << lucy;
	return 0;
}

这里会直接报错,找不到该运算符的一个函数,该运算符无法对类的对象进行访问操作。

于是我们重载<<

void operator<<(ostream &out,person &lucy)//这里用到引用,还要友元
{
	out << lucy.num <<" " << lucy.name<<" " << lucy.score << endl;
	//返回该输出流变量
}

这里的cout的类型为ostream,我们参数直接定义运算符两边的类型的变量,这里直接利用引用,避免指针的麻烦使用。

因为这里访问的变量为私有的数据,如我们在类里还需要定义为友元函数。

我们可以从此理解所谓的运算符重载就是重新定一个函数,让操作符可以实现对数据的操作,而函数名我们用operator+运算符来表示。可以看到这里的函数仅仅是打印出数据,故在使用时,对于操作对象的<<是无法在与endl结合换行,因为该运算符的函数重载中只有打印出数据的功能。

那么如何让他拥有输出流类型的操作,这里我们可以改变重载函数类型:

ostream& operator<<(ostream &out,person &lucy)//这里用到引用,还要友元
{
	out << lucy.num <<" " << lucy.name<<" " << lucy.score << endl;
	//返回该输出流变量
    return out;
}

这里返回的新out是输出流类型,我们在使用时就可以

int main()
{
	person lucy(100, "lucy", 99.5);
	//这里想要输出数据num,是不可以的,私有数据
	//要想访问两种方法:1.利用成员函数  2.重载运算符

	//cout << lucy;  直接使用会报错,找不到该运算符的一个函数
    
	//程序运行对于<<进行查找若果类型左边是输出流,右边是person类,则会自动调用重载后的<<,反之,是默认的输出运算符
	cout << lucy;//重载后的<<是链式操作,此时这里无法在添加endl.
	//若想使用输出流类型的写法,函数类型为输出流,因为输出流可以和endl结合。
	cout << lucy<<endl;
	return 0;
}

 还需要注意的一点关于函数类型后跟&:

在重载运算符时,为了避免不必要的对象拷贝,函数类型后面要跟&符号。这样可以将参数传递给函数时,传递的是对象的引用而不是对象的副本,从而避免了对象的拷贝操作,提高了程序的效率。此外,使用引用还可以避免修改对象的副本而不是实际对象的问题。因此,在重载运算符时,函数类型后面要跟&符号。
 

4.重载>>(输入)运算符(全局函数实现)

同输出运算符同理:

这里为了观察显眼,参数用的lucy,其实只要是传的类的对象就行。

class person
{
	friend ostream& operator<<(ostream& out, person& lucy);
	friend void operator>>(istream& in, person& lucy);
	friend istream& operator>>(istream& in, person lucy);
private:
	int num;
	string name;
	float score;
public:
	person() {}
	person(int num, string name, float score) :num(num), name(name), score(score){}
	void showperson(int num, string name, float score)
	{
		cout << num << endl;
		cout << name << endl;
		cout << score << endl;
	}
};

ostream& operator<<(ostream &out,person &lucy){
	out << lucy.num <<" " << lucy.name<<" " << lucy.score << endl;
	
}

 重载输入运算符:

void operator>>(istream& in, person lucy)
{
	cin >> lucy.num >> lucy.name >> lucy.score;
}

 主函数调用:

int main()
{
	person lucy;
	cin >> lucy;
	cout << lucy<<endl;
	return 0;
}

同样需要注意的是:因为这里的重载函数定义的类型是无返回类型,无法实现链接的功能,比如同时对多个对象输入

int main()
{
	person lucy;
    person bob;
	cin >> lucy >> bob;
	return 0;
}

还是改变函数类型

istream& operator>>(istream& in, person& lucy)
{
	cin >> lucy.num >> lucy.name >> lucy.score;
    return in;
}

5. 重载++/--运算符

重载++和--运算符时,有点让人不知所措,因为我们总希望能根据他们出现在对象的前面还是后面而来调用不同的重载函数,例如当看见++a,会调用前置加加重载函数,看见a++,会调用后置加加的重载函数。

++a ; 先自加 在使用

a++;//先使用 在自加

首先理解前置加加与后置加加的实质区别:

.前置加加返回的是引用

.后置加加返回的是对象

其次:

++无论前置还是后置,我们发现他的函数重载时设计的参数将会是一样,但这样就无法确定该调用哪一个,考虑到要让参数不一样而来调用对象的前置函数还是后置函数,于是多利用了一个占位参数

.前置加加调用TYPE& operator++()函数  比如++a,调用operator++(a)

.后置加加调用的是TYPE operator++(int)函数,也就是后置加加多了一个占位参数 .比如a++,调用

operator++(a,int)

.前置加加调用TYPE& operator--()函数  比如--a,调用operator--(a)

.后置加加调用的是TYPE operator--(int)函数,也就是后置加加多了一个占位参数 .比如a--,调用

operator--(a,int).

#define _CRT_SECURE_NO_WARNINGS 
#include <iostream>
 #include <string.h>
 #include <stdlib.h>
 #include <string>
 using namespace std;
 class Myint
 {
 public:
	 Myint(int num)//this
		 {
		 this-> num = num;
		 }
	 //重载前置加加
	 //先加加,再反回this
	 Myint& operator++()
		 {
		 this-> num++;
		 return *this;
		 }
	 //重载后置加加
	 //先保存当前的this,在加加,返回当前的this
	 Myint operator++(int)
		 {
		 Myint tmp = *this;
		this-> num++;
		 return tmp;
	      }
	 int num;
	 };
 //重载输出流<<
 ostream& operator<<(ostream & cout, Myint & p)
 {
	 cout << p.num;
	 return cout;
	
 }
 void test01()
 {
	 Myint p1(10);
	 cout << p1.num << endl;
	 //operator++(p1)    p1.operator++()
	 Myint p2 = p1++;
	 cout << p2 << endl;
	 Myint p3 = ++p1;
	 cout << p3 << endl;
 }
 int main()
{
 test01();
 return 0;
}

同理我们重载一下--

#define _CRT_SECURE_NO_WARNINGS 
#include <iostream>
 #include <string.h>
 #include <stdlib.h>
 #include <string>
 using namespace std;
 class Myint
 {
 public:
	 Myint(int num)//this
		 {
		 this-> num = num;
		 }
	 //重载前置加加
	 //先加加,再反回this
	 Myint& operator--()
		 {
		 this-> num--;
		 return *this;
		 }
	 //重载后置加加
	 //先保存当前的this,在加加,返回当前的this
	 Myint operator--(int)
		 {
		 Myint tmp = *this;
		this-> num--;
		 return tmp;
	      }
	 int num;
	 };
 //重载输出流<<
 ostream& operator<<(ostream & cout, Myint & p)
 {
	 cout << p.num;
	 return cout;
 }
 void test01()
 {
	 Myint p1(10);
	 cout << p1.num << endl;
	 //operator--(p1)    p1.operator--()
	 Myint p2 = p1--;
	 cout << p2 << endl;
	 Myint p3 = --p1;
	 cout << p3 << endl;
 }
 int main()
{
 test01();
 return 0;
}

 

6.重载==运算符

举例:判断person 对象lucy是否等于person对象bob,若相等打印相等,否则打印不相等

class person
{
	friend ostream& operator<<(ostream& out, person& lucy);
	friend istream& operator>>(istream& in, person& lucy);
	
	//friend bool operator==(person& oa, person& ob);
private:
	int num;
	string name;
	float score;
public:
	person() {}
	person(int num, string name, float score) :num(num), name(name), score(score){}
	void showperson(int num, string name, float score)
	{
		cout << num << endl;
		cout << name << endl;
		cout << score << endl;
	}   
	//成员函数重载operator==
	bool operator==(person &ob)
	{
		if (num == ob.num && name == ob.name && score == ob.score)
			return true;
		else
			return false;
	}
};
//全局函数重载operator==
//bool operator==(person &oa,person& ob)
//{
//	if (oa.num == ob.num && oa.name == ob.name && oa.score == ob.score)
//		return true;
//	else
//		return false;
//}
ostream& operator<<(ostream &out,person &lucy)//这里用到引用,还要友元
{
	out << lucy.num <<" " << lucy.name<<" " << lucy.score << endl;
	return out;
}
istream& operator>>(istream& in, person& lucy)
{
	in >> lucy.num >> lucy.name >> lucy.score;
	return in;
}
//判断lucy是否等于bob,若相等打印相等,否则打印不相等
 
int main()
{
	person lucy;
	person bob;
	cin >> lucy>>bob;
	if (lucy == bob)
	{
		cout << "相等" << endl;
	}
	else
	{
		cout << "不相等" << endl;
	}

	cout << lucy<<endl;
	return 0;
}

 

7.可以重载的运算符

几乎C中所有的运算符都可以重载,但运算符重载的使用时相当受限制的。特别是不能使用C中当前没有意义的运算符(例如用**求幂)不能改变 运算符优先级,不能改变运算符的参数个数。这样的限制有意义,否 则,所有这些行为产生的运算符只会混淆而不是澄清寓语意。

需要注意的是:

对于逻辑与与逻辑或,因为我们无法实现他的短路特性,一般不存在对它们的重载。

 

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

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

相关文章

研读Rust圣经解析——Rust learn-16(高级trait,宏)

研读Rust圣经解析——Rust learn-16&#xff08;高级trait&#xff0c;宏&#xff09; 高级trait关联类型Type为什么不用泛型而是Type 运算符重载&#xff08;重要等级不高&#xff09;重名方法消除歧义never typecontinue 的值是 ! 返回闭包 宏自定义宏&#xff08;声明宏&…

day7 实现TCP通信

目录 函数介绍 代码实现 函数介绍 socket函数与通信域&#xff1a; #include <sys/types.h> #include <sys/socket.h> int socket(int domain, int type, int protocol); -domain&#xff1a;指定通信域&#xff08;通信地址族&#xff09;&#xff1b; AF_I…

python-chatgpt自动化批量改写文章-基于gpt-3-5-turbo模型

作者&#xff1a;虚坏叔叔 博客&#xff1a;https://xuhss.com 早餐店不会开到晚上&#xff0c;想吃的人早就来了&#xff01;&#x1f604; 一、ChatGPT官方文档介绍&#xff1a; ChatGPT API—0.002美元&#xff0c;1000个token。比之前的GPT-3.0&#xff0c;成本直接降低了9…

vue3.2+ts错误:找不到模块“./App.vue”或其相应的类型声明。ts(2307)

报错原因&#xff1a; 未定义 .vue文件的类型&#xff0c;导致 ts 无法解析其类型&#xff0c;在vite-env.d.ts中定义后即可解决。 解决方法&#xff1a; 找到项目src目录下的vite-env.d.ts 文件&#xff0c;追加以下内容&#xff1a; declare module "*.vue" {impor…

【Python】chinese_calendar包的介绍和使用案例介绍(含代码)

一、问题引入 在我们的比赛中,我们对应的有时间数据,我们需要考虑不同时间段(例如月头、月中、月末等)产品需求量有何特性,节假日对产品需求量的影响,促销(如618、双十一等)对产品需求量的影响,季节因素对产品需求量的影响等。 但是我们的数据集中,却没有这种相关的…

PyTorch中的交叉熵函数 CrossEntropyLoss的计算过程

CrossEntropyLoss() 函数联合调用了 nn.LogSoftmax() 和 nn.NLLLoss()。 关于交叉熵函数的公式详见&#xff1a; 交叉熵损失函数原理详解 CrossEntropyLoss() 函数的计算过程可以拆解为如下四个步骤&#xff1a; 1、对输出的结果进行softmax操作,因为softmax操作可以将所有输入…

【Java基础教程】初识Java

作者简介&#xff1a; 辭七七&#xff0c;目前大一&#xff0c;正在学习C/C&#xff0c;Java&#xff0c;Python等 作者主页&#xff1a; 七七的个人主页 **文章收录专栏&#xff1a;Java.SE&#xff0c;本专栏主要讲解运算符&#xff0c;程序逻辑控制&#xff0c;方法的使用&a…

Java实现数据压缩所有方式性能测试

目录 1 BZip方式1.1 引入依赖1.2 BZip工具类代码1.3 BZip2工具类代码 2 Deflater方式3 Gzip方式4 Lz4方式4.1 简介4.2 算法思想4.3 算法实现4.3.1 lz4数据格式2、lz4压缩过程3、lz4解压过程 4.4 Lz4-Java4.4.1 简介4.4.2 类库 5 SevenZ方式5.1 引入依赖5.2 工具类代码 6 Zip方式…

C++(继承和组合)

继承&#xff1a;public继承是一种 is-a 的关系&#xff0c;也就是每一个派生类对象都有一个基类对象 这些关系都适合用继承来表达 ----> 继承了之后父类的成员就变成了子类的一部分&#xff0c;子类对象可以直接用 组合&#xff1a; 是一种has -a&#xff08;有一个&…

GraphSAGE聚合流程计算实例

本篇中我们只讨论聚合流程&#xff0c;不考虑GraphSAGE的小批量训练等内容。 我们先来看一下GraphSAGE的聚合流程伪代码&#xff0c;之后会给出两个具体的计算例子进行说明&#xff1a; 11行中&#xff0c; N ( k ) ( u ) N^{(k)}(u) N(k)(u)表示节点u的邻居节点采样函数&…

力扣杯2023春·个人赛

文章目录 力扣杯2023春-个人赛[LCP 72. 补给马车](https://leetcode.cn/problems/hqCnmP/)模拟 [LCP 73. 探险营地](https://leetcode.cn/problems/0Zeoeg/)模拟 哈希 [LCP 74. 最强祝福力场](https://leetcode.cn/problems/xepqZ5/)二维差分 离散化扫描线 [LCP 75. 传送卷轴…

CANOE入门到精通——CANOE系列教程记录1 第一个仿真工程

本系列以初学者角度记录学习CANOE&#xff0c;以《CANoe开发从入门到精通》参考学习&#xff0c;CANoe16 demo版就可以进行学习 概念 CANoe是一种用于开发、测试和分析汽车电子系统的软件工具。它通过在不同层次上模拟汽车电子系统中的不同部件&#xff0c;如ECU、总线和传感…

自动化运维工具Ansible之playbook剧本

目录 一、playbook 1、playbook简述 2、playbook剧本格式 3、playbook组成部分 4、playbook启动及检测 5、playbook模块实战实例1 6、vars模块实战实例2 7、when模块实战实例3 8、with_items循环模块实战实例4 9、template模块实战实例5 10、tags模块实战实例6 一、…

VM中kali虚拟机创建docker部署WebGoat

这里选择在docker中配置&#xff08;因为方便&#xff09; 首先下载docker sudo apt-get install docker.io 然后从Docker Hub下载WebGoat 8.0 的docker镜像 使用命令 docker pull webgoat/webgoat-8.0 完成后查看现在kali虚拟机中的docker镜像列表 输入命令 docker images …

0704一阶线性微分方程-微分方程

文章目录 1 线性方程1.1 定义1.2 解法&#xff08;常数变易法&#xff09;1.3 例题 2伯努利方程3 简单变量替换解方程结语 1 线性方程 1.1 定义 一阶微分方程&#xff1a;形式上能化成 d y d x P ( x ) y Q ( x ) \frac{dy}{dx}P(x)yQ(x) dxdy​P(x)yQ(x)的方程&#xff0c;…

树莓派CSI摄像头使用python调用opencv库函数进行运动检测识别

目录 一、完成摄像头的调用 二、利用python调用opencv库函数对图像进行处理 2.1 图像处理大体流程 2.2 opencv调用函数的参数以及含义 2.2.1 ret, img cap.read() 读取帧图像 2.2.2 cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) 灰度图像 2.2.3 gray_diff_img cv2.absdiff(g…

详解子网划分练习题(32道)

目录 1 子网划分概念&#xff1a; 2 划分方法&#xff1a; 子网划分方法&#xff1a;段&#xff0c;块&#xff0c;数的计算三步。 段就是确定ip地址段中既有网络地址&#xff0c;又有主机地址的那一段是四段中的那一段&#xff1f; 块就确定上一步中确定的那一段中的主机…

【Linux】网络配置详细步骤及其相关基础知识介绍

一、Linux网络配置步骤 1、登录root账户 进行网络配置需要使用root权限&#xff0c;因此需要先登录root用户 2、输入ip addr查看网络信息 只有一个本机地址127.0.0.1&#xff0c;因为Linux操作系统的网卡开关还没有打开。 3、输入cd /etc/sysconfig/network-scripts/进入目录…

R语言 | 列表

目录 一、建立列表 1.1 建立列表对象——对象元素不含名称 1.2 建立列表对象——对象元素含名称 1.3 处理列表内对象的元素名称 1.4 获得列表的对象元素个数 二、获取列表内对象的元素内容 2.1 使用"$"符号取得列表对象的元素内容 2.2 使用"[[ ]]"符…

关于GeoServer发布服务时数据源设置的避坑指南

题外话 时光任然&#xff0c;一年一度的五一劳动节已然来到。作为疫情之后迎来的第一个五一&#xff0c;不知道各位小伙伴们怎么度过这个劳动节呢&#xff1f;是决定去另一个城市&#xff0c;观察体验一下不一样的风景&#xff0c;或者去旅游&#xff0c;给自己放假。昨天被123…