文章目录
- 前情回顾
 - 运算符重载
 - 概念
 - 为什么会出现运算符重载
 - 运算符重载中函数名格式
 - 加减运算符重载
 - 作用
 - 实现
 
- 左移运算符重载
 - 作用
 - 左移运算符是什么?
 - 实现
 
- 递增递减运算符
 - 作用
 - 实现
 - 前置
 - 后置
 
- 赋值运算符重载
 - 关系运算符重载
 - 作用
 - 实现
 
- 函数调用运算符重载
 
- 第二种重载掌握!突破
 - 本章知识点(图片形式)
 
🎉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++中,编译器一般会给一个类默认提供四个函数
 
- 默认构造函数(无参,函数体为空)
 - 默认析构函数(无参,函数体为空)
 - 默认拷贝函数,对属性进行浅拷贝
 - 赋值运算符,对属性进行浅拷贝
 
注意!:
- 当这个时候属性内的值有属性指向堆区时,赋值操作符也会产生深拷贝和浅拷贝问题
 
编译器提供的是浅拷贝,这个时候在析构函数内进行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++爬塔日记
🙉都看到这里了,留下你们的👍点赞+⭐收藏+📋评论吧🙉

















