为什么要有运算符重载?
观察下列代码,当我们要比较两个日期类(自定义类型)的大小的时候,我们没法使用编译器自带的小于<
符号来比较,就像这样的形式:d1 < d2
我们需要自己写一个函数来进行比较,这是很麻烦的。我们想要编译器来帮我们比较,而不需要自己去写一个函数。这个时候就要用到运算符重载。
//需要自己写一个函数来比较。
bool DateLess(const Date& x1, const Date& x2)
{
if (x1._year < x2._year)
return true;
else if (x1._year == x2._year && x1._month < x2._month)
return true;
else if (x1._year == x2._year && x1._month == x2._month && x1._day < x2._day)
return true;
else
return false;
}
int main()
{
Date d1(2023, 7, 23);
Date d2(2023, 8, 21);
//比较两个日期类的大小
cout << DateLess(d1, d2) << endl;
return 0;
}
如何操作呢?
使用operator<
来命名函数,(operator< :是一个特殊的成员函数相当于对 运算方式的重新定义)这样一来,在使用的时候就可以使用
<
符号了。
注意事项:
c++引入运算符重载是为了增强代码的可读性才引入的
运算符重载是具有特殊函数名的函数,也具有其相应的返回值类型,函数名字以及参数列表,其返回值的类型与参数列表和普通的函数类似。
函数的名字为:operator+需要重载的运算符号
。
函数原型:返回值类型 opera操作符(参数列表)
注意:
operator
后面的操作符必须是以前就有实际意义的操作符(就像<
,>
,+
,*
,/
。)不能跟其他一些无意义的操作符号比如:'@
'符号。- 运算符重载至少有一个参数必须是自定义类型。
- 运算符重载不能改变操作符的操作数的个数,打个比方:
小于号'<'
有两个操作数,左边一个,右边一个。比如:a < b
, 你不能把它改变成三个操作数:ab<c
++
只有一个操作数。比如i++
你不能把它改变成两个操作数:i++j
一个操作符是几个操作数,那么重载之后他也就该有几个操作数不会增加,也不会减少。 - 下面这几个运算符是不能重载的:
(1)域作用限定符不能重载:::
(2)sizeof
不能重载
(3)三目运算符不能重载:?
:
(4)点不能重载:.
(对象变量之类的,取他的成员就用了点操作符)
(5)点星不能重载:.*
(这里的点星是连在一起的,是一个整体。)
(6)星是可以重载的,*
有坑未填
上面那个operator函数
的实际场景我并未写出来,下面,我把所有代码拿出来看看:
#include<iostream>
using namespace std;
//日期类
class Date
{
public:
//默认构造
Date(int year = 1, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
//拷贝构造函数 用同类型的对象来初拷贝始化它,所以叫拷贝构造
Date(Date& d)
{
cout << "Date(Date& d)" << endl;
_year = d._year;
_month = d._month;
_day = d._day;
}
void Print()
{
cout << _year << "/" << _month << "/" << _day << endl;
}
//没填的坑就在这里,注意看!!!!!!!!!!!!!!!!!!!!
//没填的坑就在这里,注意看!!!!!!!!!!!!!!!!!!!!
//没填的坑就在这里,注意看!!!!!!!!!!!!!!!!!!!!
//没填的坑就在这里,注意看!!!!!!!!!!!!!!!!!!!!
//private: //这里把private注释掉了
int _year;
int _month;
int _day;
};
//这个就是我们写的那个重载运算符的函数
bool operator<(const Date& x1, const Date& x2)
{
if (x1._year < x2._year)
return true;
else if (x1._year == x2._year && x1._month < x2._month)
return true;
else if (x1._year == x2._year && x1._month == x2._month && x1._day < x2._day)
return true;
else
return false;
}
int main()
{
Date d1(2023, 7, 23);
Date d2(2023, 8, 21);
cout << (d1 < d2) << endl;
return 0;
}
首先我们来解释我们为什么要把private
注释掉?
我们知道private
是限制访问的。它里面修饰的是成员变量。成员变量是不允许外界访问的。
可是当我们调用运算符重载的那个operator<
函数的时候,我们需要给这个函数传参吧?
但是我们给这个函数传的参数是类里面的成员变量。
所以说这个时候我们要访问成员变量。但是private
阻止了我们,不许我们访问。所以说我们把它注释掉了。
但是这始终不是个办法呀。我们还是希望private
能继续发挥他的作用。因为private
能阻止其他的一些非法访问。
所以我们现在的需求变成了这样:
我们希望private
能继续发挥它的功能,同时我们也希望operator<
函数能继续访问成员变量。
那怎么办?
我们知道成员函数
是可以访问成员变量
的。
所以我们只需要把operator<
变成成员函数
就行了,即:把它放在类
里面。
但是这个时候又会出现一个问题。当你运行的时候,编译器会报错,说你的operator<
操作数太多了。
但是你一看,诶,你就只有两个操作数呀?没有变多呀。这是怎么回事?
你难道忘了在类
面有一个隐藏的参数:this指针
呀
所以啊就变成了三个操作数了。
所以这个时候你要删掉一个操作数就行了
这样就行了:
// 这个时候这个函数看似只有一个参数,实则有两个参数
//因为还有一个隐藏的this 指针
bool operator<(const Date& d) //删了一个操作数后
{
if (_year < d._year)
return true;
else if (_year == d._year && _month < d._month)
return true;
else if (_year == d._year && _month == _month && _day < d._day)
return true;
else
return false;
}
这个时候当编译器看到上面的这串代码,实际上是这样的
:d1 < d2
会被转化为
:d1 . operator<(d2)
这个时候this指针
是指向d1
的