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

🎉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++爬塔日记
🙉都看到这里了,留下你们的👍点赞+⭐收藏+📋评论吧🙉

评论 29
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

封心锁爱的前夫哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值