第五层:C++中的运算符重载

news2024/12/23 11:27:56

文章目录

  • 前情回顾
  • 运算符重载
    • 概念
    • 为什么会出现运算符重载
    • 运算符重载中函数名格式
    • 加减运算符重载
      • 作用
      • 实现
    • 左移运算符重载
      • 作用
      • 左移运算符是什么?
      • 实现
    • 递增递减运算符
      • 作用
      • 实现
        • 前置
        • 后置
    • 赋值运算符重载
    • 关系运算符重载
      • 作用
      • 实现
    • 函数调用运算符重载
  • 第二种重载掌握!突破
  • 本章知识点(图片形式)

🎉welcome🎉
✒️博主介绍:一名大一的智能制造专业学生,在学习C/C++的路上会越走越远,后面不定期更新有关C/C++语法,数据结构,算法,Linux,ue5使用,制作游戏的心得,和大家一起共同成长。
✈️C++专栏:C++爬塔日记
😘博客制作不易,👍点赞+⭐收藏+➕关注

前情回顾

第四层中,我遇到了一个很新奇的力量,叫做友元,可以把一些人变成好朋友的神奇力量,除此之外,还学习到了,类内声明函数,类外进行定义的操作,同时,也踏入了第五层…

  • 🚄上章地址:第四层:友元与函数成员别样定义

运算符重载

黑黝黝的洞口,一个人影在缓缓出现,“我就知道你肯定可以掌握友元的,这次需要你掌握另一种重载的力量——运算符重载,祝你好运…”。“新的重载吗…”。

概念

  • 对已有的运算符进行重新定义,赋予其另一种功能,以适应各种不同的运算类型

为什么会出现运算符重载

  • 对于内置的数据类型,编译器知道如何运算,但是对于自定义类型,是不知道如何去运算的,这个时候就需要运算符重载,本质上,是写一个函数来告诉编译器。

运算符重载中函数名格式

  • 对于运算符重载中,上面提到,本质上是写一个函数,不同的人对于函数的命名方式是不一样的,这样编译器也不好识辨,这个函数是不是在实现运算符重载,所以编译器提供了一个固定的格式:
  • operator+(这里这个加号可以替换成别的符号,当你要对什么符号进行运算符重载时就用什么符号)

加减运算符重载

作用

  • 实现两个自定义数据类型的相加减运算

实现

上面提到对于内置数据类型编译器可以知道如何去运算,而自定义类型是不知道的,那现在有一个类,它有三个对象,其中一个对象等于其他两个对象加起来,要怎么实现,可以先验证直接用+能不能进行:

#include<iostream>
using namespace std;

class A
{
public:
	A()
	{

	}
	A(int a, int b)
	{
		_a = a;
		_b = b;
	}
	int _a;
	int _b;
};

void test1()
{
	A a1(10, 10);
	A a2(10, 10);
	A a3;
	a3 = a1 + a2;
}
int main()
{
	test1();
	return 0;
}

在这里插入图片描述
报错了,没有与这些操作数匹配的“+”运算符,这个错误说明了编译器是不知道对象内部怎么进行加减的,那设计一个函数,函数名字用上面提到的operate+:

#include<iostream>
using namespace std;

class A
{
public:
	A()
	{

	}
	A(int a, int b)
	{
		_a = a;
		_b = b;
	}
	A operator+(A &a1)
	{
		A tmp;
		tmp._a = this->_a + a1._a;
		tmp._b = this->_b + a1._b;
		return tmp;
	}
	int _a;
	int _b;
};

void test1()
{
	A a1(10, 10);
	A a2(10, 10);
	A a3;
	a3 = a1 + a2;
	cout << a3._a<<"   "<< a3._b;
}
int main()
{
	test1();
	return 0;
}

在这里插入图片描述
其实这中写法的本质还是调用函数,即:

  • a3=a1+a2 == a3=a1.operator+(a2)

同时也可以把这个改成类外的全局函数,这个时候传参就需要传两个,内部改一下:

#include<iostream>
using namespace std;

class A
{
public:
	A()
	{

	}
	A(int a, int b)
	{
		_a = a;
		_b = b;
	}
	//A operator+(A &a1)
	//{
	//	A tmp;
	//	tmp._a = this->_a + a1._a;
	//	tmp._b = this->_b + a1._b;
	//	return tmp;
	//}
	int _a;
	int _b;
};
A operator+(A& a1, A& a2)
{
	A tmp;
	tmp._a = a1._a + a2._a;
	tmp._b = a1._b + a2._b;
	return tmp;
}

void test1()
{
	A a1(10, 10);
	A a2(10, 10);
	A a3;
	a3 = a1 + a2;
	cout << a3._a<<"   "<< a3._b;
}
int main()
{
	test1();
	return 0;
}

在这里插入图片描述
也是可以正常跑起来的。
减号同理,与加号实现原理一致,只需要把+换成-。

#include<iostream>
using namespace std;

class A
{
public:
	A()
	{

	}
	A(int a, int b)
	{
		_a = a;
		_b = b;
	}
	//A operator+(A &a1)
	//{
	//	A tmp;
	//	tmp._a = this->_a + a1._a;
	//	tmp._b = this->_b + a1._b;
	//	return tmp;
	//}
	int _a;
	int _b;
};
A operator-(A& a1, A& a2)
{
	A tmp;
	tmp._a = a1._a - a2._a;
	tmp._b = a1._b - a2._b;
	return tmp;
}

void test1()
{
	A a1(10, 10);
	A a2(10, 10);
	A a3;
	a3 = a1 - a2;
	cout << a3._a<<"   "<< a3._b;
}
int main()
{
	test1();
	return 0;
}

在这里插入图片描述

左移运算符重载

作用

  • 可以输出自定义的数据类型

左移运算符是什么?

  • 左移运算为程序员调用cout的时候,会在后面加“<<”,这个就是左移运算符

实现

同样,可以直接用cout来进行只用类就把类内属性都打印出来吗:

#include<iostream>
using namespace std;

class A
{
public:
	A()
	{

	}
	A(int a, int b)
	{
		_a = a;
		_b = b;
	}
	//A operator+(A &a1)
	//{
	//	A tmp;
	//	tmp._a = this->_a + a1._a;
	//	tmp._b = this->_b + a1._b;
	//	return tmp;
	//}
	int _a;
	int _b;
};
A operator-(A& a1, A& a2)
{
	A tmp;
	tmp._a = a1._a - a2._a;
	tmp._b = a1._b - a2._b;
	return tmp;
}

void test1()
{
	A a1(10, 10);
	A a2(10, 10);
	A a3(0, 0);
	a3 = a1 - a2;
	cout << a3 << endl ;
}
int main()
{
	test1();
	return 0;
}

在这里插入图片描述

明显是不可以的,那要怎么样进行改造呢?如果是成员函数呢?传参和返回类型是什么?返回类型暂定为void,那参数呢?cout吗?那传参cout简化就变成了:

  • 对象<<cout

放的顺序不一样,所以成员函数无法实现,那就用全局函数,用之前,需要探究一个事情,cout是什么类型?转到定义看一下:

在这里插入图片描述
cout的类型就是ostream,那类型解决了,还有什么注意事项吗?有,并且很重要:

  • 对于全局来说,只有一个cout,所以cout是需要进行引用的,对象也是需要进行引用才可以。
#include<iostream>
using namespace std;

class A
{
public:
	A()
	{

	}
	A(int a, int b)
	{
		_a = a;
		_b = b;
	}
	//A operator+(A &a1)
	//{
	//	A tmp;
	//	tmp._a = this->_a + a1._a;
	//	tmp._b = this->_b + a1._b;
	//	return tmp;
	//}
	int _a;
	int _b;
};
A operator-(A& a1, A& a2)
{
	A tmp;
	tmp._a = a1._a - a2._a;
	tmp._b = a1._b - a2._b;
	return tmp;
}
void operator<<(ostream& cout, A& a3)
{
	cout << a3._a << "   " << a3._b;
}
void test1()
{
	A a1(10, 10);
	A a2(10, 10);
	A a3(0, 0);
	a3 = a1 - a2;
	cout << a3 << endl;
}
int main()
{
	test1();
	return 0;
}

在这里插入图片描述
为什么这里还是有报错?对于cout这种能一直在后面追加编程,称作链式编程,在前面的this指针中,提到过一种用途,这里也要用到,因为返回的是void,所以对于endl来说,前面的就不是cout,所以就不能进行输出,那这里就需要返回一个cout,cout的类型,同时记得加引用:

#include<iostream>
using namespace std;

class A
{
public:
	A()
	{

	}
	A(int a, int b)
	{
		_a = a;
		_b = b;
	}
	//A operator+(A &a1)
	//{
	//	A tmp;
	//	tmp._a = this->_a + a1._a;
	//	tmp._b = this->_b + a1._b;
	//	return tmp;
	//}
	int _a;
	int _b;
};
A operator-(A& a1, A& a2)
{
	A tmp;
	tmp._a = a1._a - a2._a;
	tmp._b = a1._b - a2._b;
	return tmp;
}
ostream& operator<<(ostream& cout, A& a3)
{
	cout << a3._a << "   " << a3._b;
	return cout;
}
void test1()
{
	A a1(10, 10);
	A a2(10, 10);
	A a3(0, 0);
	a3 = a1 - a2;
	cout << a3 << endl;
}
int main()
{
	test1();
	return 0;
}

在这里插入图片描述

递增递减运算符

作用

  • 通过重载递增递减预算符,实现自己类内的整型数据

实现

前置

前置的递增递减是前++(- -),在使用,所以可以先在实现的函数内部先进行++(- -),在返回引用,这里为什么要返回引用呢?如果不返回引用,那就是对局部这个变量进行了++(- -),多次使用++(- -),本身只会生成一次,因为不是引用,会产生拷贝,把数据拷贝到新的空间上:

  • 使用引用
#include<iostream>
using namespace std;

class A
{
public:
	A()
	{

	}
	A(int a, int b)
	{
		_a = a;
		_b = b;
	}
	A& operator++()
	{
		this->_a++;
		this->_b++;
		return *this;
	}
	//A operator+(A &a1)
	//{
	//	A tmp;
	//	tmp._a = this->_a + a1._a;
	//	tmp._b = this->_b + a1._b;
	//	return tmp;
	//}
	int _a;
	int _b;
};
A operator-(A& a1, A& a2)
{
	A tmp;
	tmp._a = a1._a - a2._a;
	tmp._b = a1._b - a2._b;
	return tmp;
}
ostream& operator<<(ostream& cout, A& a3)
{
	cout << a3._a << "   " << a3._b;
	return cout;
}
void test1()
{
	A a1(10, 10);
	A a2(10, 10);
	A a3(0, 0);
	a3 = a1 - a2;
	cout << ++(++a3) << endl;
}
int main()
{
	test1();
	return 0;
}

在这里插入图片描述

  • 不使用引用
#include<iostream>
using namespace std;

class A
{
public:
	A()
	{

	}
	A(int a, int b)
	{
		_a = a;
		_b = b;
	}
	A operator++()
	{
		this->_a++;
		this->_b++;
		return *this;
	}
	//A operator+(A &a1)
	//{
	//	A tmp;
	//	tmp._a = this->_a + a1._a;
	//	tmp._b = this->_b + a1._b;
	//	return tmp;
	//}
	int _a;
	int _b;
};
A operator-(A& a1, A& a2)
{
	A tmp;
	tmp._a = a1._a - a2._a;
	tmp._b = a1._b - a2._b;
	return tmp;
}
ostream& operator<<(ostream& cout, A& a3)
{
	cout << a3._a << "   " << a3._b;
	return cout;
}
void test1()
{
	A a1(10, 10);
	A a2(10, 10);
	A a3(0, 0);
	a3 = a1 - a2;
	cout << ++(++a3) << endl;
}
int main()
{
	test1();
	return 0;
}

在这里插入图片描述
直接是报错的。
前置递减实现也相同,将++换成–即可。

后置

当定义后置++的时候,发现,与前置++使用的是一样的名字,发现了重定义,那这个时候怎么办?可以在参数中写一个int,用于区分,这个int属于占位参数,用于区分前置递增和后置递增,编译器是只认int的,对于后置递增,应该记录初始结果,如果在递增,返回初始结果,这个时候应该返回值,而不是引用,因为返回的是一个局部变量,如果返回引用,就会非法访问:

  • 不是用引用
#include<iostream>
using namespace std;

class A
{
public:
	A(int a)
	{
		_a = a;
	}
	//后置++
	A operator++(int)
	{
		int a = this->_a++;
		return a;
	}
	int _a;
};
void test1()
{
	A a1(10);
	cout << a1._a++ << endl;
	cout << a1._a << endl;
}
int main()
{
	test1();
	return 0;
}

在这里插入图片描述

  • 是引用
#include<iostream>
using namespace std;

class A
{
public:
	A(int a)
	{
		_a = a;
	}
	//后置++
	A& operator++(int)
	{
		int a = this->_a++;
		return a;
	}
	int _a;
};
void test1()
{
	A a1(10);
	cout << a1._a++ << endl;
	cout << a1._a << endl;
}
int main()
{
	test1();
	return 0;
}

在这里插入图片描述

赋值运算符重载

  • 其实在C++中,编译器一般会给一个类默认提供四个函数
  1. 默认构造函数(无参,函数体为空)
  2. 默认析构函数(无参,函数体为空)
  3. 默认拷贝函数,对属性进行浅拷贝
  4. 赋值运算符,对属性进行浅拷贝

注意!

  • 当这个时候属性内的值有属性指向堆区时,赋值操作符也会产生深拷贝和浅拷贝问题

编译器提供的是浅拷贝,这个时候在析构函数内进行delete释放内存时,会出现内存释放两次,所以这个时候operator=修改成深拷贝即可,注意,在赋值操作符之前,应该先判断赋值左侧值内堆区是否有数据,有数据要先释放干净,在进行深拷贝,记得返回值要返回自身:

#include<iostream>
using namespace std;

class A
{
public:
	A()
	{
		p = new int;
	}
	~A()
	{
		delete p;
		p = NULL;
	}
	void operator=(A& a)
	{
		if (p != NULL)
		{
			p = NULL;
		}
		p = new int(*a.p);
	}
	int* p;
};

void test1()
{
	A a;
	*a.p = 10;
	A b;
	b = a;
	cout << *b.p << endl;
}
int main()
{
	test1();
	return 0;
}

在这里插入图片描述

关系运算符重载

作用

  • 可以人两个自定义类型进行比较操作

实现

只用对比每一组属性是否相等,相等返回真,不相等返回假:

#include<iostream>
using namespace std;

class A
{
public:
	A(int a, char c)
	{
		_a = a;
		_c = c;
	}
	bool operator==(A& a)
	{
		if (_a == a._a && _c == a._c)
		{
			return 1;
		}
		return 0;
	}
	int _a;
	char _c;
};
void test1()
{
	A a(10, 'c');
	A b(10, 'c');
	if (a == b)
	{
		cout << "a=b" << endl;
	}
	else
	{
		cout << "a!=b" << endl;
	}
}
int main()
{
	test1();
	return 0;
}

在这里插入图片描述

函数调用运算符重载

  • 函数调用操作符()也可以重载
  • 由于重载后调用的方式和函数本身的调用相似,所以也被称为仿函数
  • 仿函数没有固定写法,函数体内可以写任何。

事例:

#include<iostream>
using namespace std;

class A
{
public:
	void operator()()
	{
		cout << "仿函数调用" << endl;
	}
};
void test1()
{
	A a;
	a();
}
int main()
{
	test1();
	return 0;
}

在这里插入图片描述

第二种重载掌握!突破

面前的石碑碎去,露出后面黑黝黝的洞口…

本章知识点(图片形式)

在这里插入图片描述

😘预知后事如何,关注新专栏,和我一起征服C++这座巨塔
🚀专栏:C++爬塔日记
🙉都看到这里了,留下你们的👍点赞+⭐收藏+📋评论吧🙉

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

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

相关文章

vueJs中toRaw与markRaw函数的使用比较

01toRaw()函数接收一个reactive响应式数据,将一个响应式的数据变为普通类型的数据,转化为非响应式数据,相当于还原对象,reactive相当于制作,但对于ref响应式数据不起作用将一个由reactive生成的响应式对象转为普通(原始)对象toRaw()可以返回由reactive(),readonly(),shallowRea…

Java_Git:1. Git简介

目录 1 Git历史 2 Git与Svn对比 2.1 Svn特点 2.2 Git特点 3 Git工作流程 4 Git的安装 4.1 软件下载 4.1.1 git 4.1.2 tortoisegit 4.2 软件安装 4.2.1 安装git for windows 4.2.2 安装TortoiseGit 4.2.3 安装TortoiseGit中文语言包 1 Git历史 版本控制系统目标&…

Spread 16.0.2 for Winforms Crack-2023.1.4 Version

Spread使用这些无依赖性的 WinForms 电子表格组件探索 WinForms 企业应用程序的可能性。 Spread新增&#xff1a;v15 NuGet 包现在支持 .NET 6.0、.NET Core 3.1 和 .NET 4.62 使用桌面设计器应用程序快速提供类似 Excel 的电子表格体验 使用全面的 API创建企业电子表格、网格…

【Python-Django】医疗辅助平台-创建项目-day1

前期准备请参考此文: https://codeknight.blog.csdn.net/article/details/126780724https://codeknight.blog.csdn.net/article/details/126780724下载BootStrap插件: Bootstrap v3 中文文档 Bootstrap 是最受欢迎的 HTML、CSS 和 JavaScript 框架,用于开发响应式布局、移…

字符串匹配算法详解

为保证代码严谨性&#xff0c;文中所有代码均在 leetcode 刷题网站 AC &#xff0c;大家可以放心食用。皇上生辰之际&#xff0c;举国同庆&#xff0c;袁记菜馆作为天下第一饭店&#xff0c;所以被选为这次庆典的菜品供应方&#xff0c;这次庆典对于袁记菜馆是一项前所未有的挑…

excel图表美化:用散点标记制作不一样的折线图

柱形图常常用于显示一段时间内的数据变化或显示各项之间的比较情况。但当时间序列过多时&#xff0c;我们往往考虑用折线图来反映数据的变化趋势。之所以讲这个&#xff0c;是希望大家能够把折线图和柱形图的应用区分开来&#xff0c;根据自己的需求使用不同的图表。以下是各个…

深度学习 GNN图神经网络(一)图的基本知识

一、前言 本文主要介绍图的一些基础知识&#xff0c;不会太深奥&#xff0c;够用就行。我们以民国最出名的七角恋人物关系图为例进行讲解。 二、图的概念 图&#xff08;Graph&#xff09;可以用来描述实体之间的关系。 如下图所示&#xff0c;一张图捋清民国最出名的七角恋…

DW动手学数据分析Task5:数据建模及模型评估

目录1 建模1.1 数据分析流程1.2 模型搭建准备工作1.2.1 导入库1.2.2 载入数据1.3 模型搭建1.3.1 选择模型1.3.2 切割训练集和测试集1.3.3 模型创建1.3.4 输出模型预测结果2 评估2.1 评估的准备工作2.2 模型评估2.2.1 交叉验证2.2.2 混淆矩阵2.2.3 ROC曲线1 建模 1.1 数据分析流…

Mac创建python2虚拟环境

前提&#xff1a;已经安装配置好python2.7版本&#xff0c;使用python和pip命令可以得到如下返回信息 1.安装virtualenv和virtualenvwrapper pip install virtualenv -i https://pypi.tuna.tsinghua.edu.cn/simple sudo pip install virtualenvwrapper -i https://pypi.tuna.t…

1595_AURIX_TC275_PMU_应用提示2

全部学习汇总&#xff1a; GreyZhang/g_TC275: happy hacking for TC275! (github.com) 如果通过标注来标注了异常字行&#xff0c;那么在算法设计的时候&#xff0c;检查到之后应该跳过这一行的数据。 可以纠正的ECC在PFlash中是可以忽略的&#xff0c;相应的信息只是可以用来…

【SpringCloud】Ribbon负载均衡的基本原理与使用

【SpringCloud】Ribbon负载均衡的基本原理与使用 一、负载均衡原理 二、源码解析 LoadBalanced IDEA源码跟踪 负载均衡源码小结 三、负载均衡策略 负载均衡策略 策略规则解析 自定义负载均衡策略 &#xff08;1&#xff09;代码方式 &#xff08;2&#xff09;配置文…

Unity的Bounds(包围盒)简记

Unity的Bounds&#xff08;包围盒&#xff09;简记一、Bounds(包围盒)概述1.什么是包围盒?2.包围盒的类型2.1 AABB包围盒(Axis-aligned bounding box)2.2 包围球(Sphere)2.3 OBB方向包围盒(Oriented bounding box)2.4 FDH固定方向凸包(Fixed directions hulls或k-DOP)2.5 包围…

云服务器部署前后端分离项目(若依)详细教程

第一次在Linux云服务器上部署前后端分离项目&#xff0c;查了很多资料和视频&#xff0c;踩了许多坑。成功实现部署若依的前后端分离项目后&#xff0c;想记录一下前后端部署的过程&#xff0c;供学习的小伙伴参考。 目录1. 环境准备2. 开放端口3. 下载前后端项目4. 前端部署5.…

Linux部署Kafka及常见问题记录

Linux部署Kafka及常见问题记录kafka 使用场景Kafka 基本概念BrokerTopic(主题)Partition(分区)ProducerConsumerConsumer Group&#xff08;消费者群组&#xff09;offset 偏移量Linux 安装&启动 kafka修改核心配置文件创建数据存放目录启动验证 kafk 是否启动成功Topic (主…

AX7A200教程(1):DDR3仿真平台搭建(一)

本章节主要调用官方的MIG控制器&#xff0c;并使用官方的MIG控制器进行仿真&#xff0c;开发环境vivado2020.1鉴于很多童鞋无法仿真自己新建的DDR工程&#xff0c;即使使用modelsim仿真也仿真失败&#xff0c;本例程着重在vivado中对自己新建的带DDR3的工程进行仿真。新建DDR3工…

Python SciPy 插值及其他各种插值法

SciPy 插值什么是插值&#xff1f;在数学的数值分析领域中&#xff0c;插值&#xff08;英语&#xff1a;interpolation&#xff09;是一种通过已知的、离散的数据点&#xff0c;在范围内推求新数据点的过程或方法。简单来说插值是一种在给定的点之间生成点的方法。例如&#x…

【MySQL】MyCAT入门综述◆掌握MyCAT的基础概念、功能及适用场景

&#x1f4eb;作者简介&#xff1a;小明java问道之路&#xff0c;专注于研究 Java/Liunx内核/C及汇编/计算机底层原理/源码&#xff0c;就职于大型金融公司后端高级工程师&#xff0c;擅长交易领域的高安全/可用/并发/性能的架构设计与演进、系统优化与稳定性建设。 &#x1f4…

二叉树知识概括锦囊(一)

作者&#xff1a;爱塔居 专栏&#xff1a;数据结构 作者简介&#xff1a;大三学生&#xff0c;希望跟大家一起进步&#xff01; 文章目录 目录 文章目录 一、树形结构 二、树的基础知识 三、二叉树 3.1 概念 3.2 特殊的二叉树 3.3 二叉树的性质 四、习题挑战 一、树形结构 树是…

【论文速递】IJCV2022 - CRCNet:基于交叉参考和区域-全局条件网络的小样本分割

【论文速递】IJCV2022 - CRCNet:基于交叉参考和区域-全局条件网络的小样本分割 【论文原文】&#xff1a;CRCNet: Few-shot Segmentation with Cross-Reference and Region-Global Conditional Networks 获取地址&#xff1a;https://link.springer.com/article/10.1007/s112…

BACnet协议详解——应用层说明一

文章目录写在前面1. 应用层模型1.1 需确认的应用层服务1.2 无需确认的应用层服务2 BACnet报文的分段2.1 报文分段规则2.1.1 APDU数据流的分段规则2.1.2 APDU最大长度的确定2.1.3 可接受的最大分段数2.2 分段协议控制信息&#xff08;PCI&#xff09;写在前面 年关将至&#xf…