运算符重载
为什么要有运算符重载
class Date
{
public:
Date(int year=2018,int month=4,int day=25)
:_year(year)
,_month(month)
,_day(day)
{}
private:
int _year;
int _month;
int _day;
}
- 我们都知道,对于内置类型,我们可以使用”+、-、*、/、++、–、……”等运算符,内置类型天然支持这些运算符
- 但是对于类似于上面我们定义的日期类等自定义类型,这些默认的操作符将不再适用;比如说我们想比较两个日期是否相等,当然我们可以使用一个函数IsEqual(const Date& d1,const Date& d2)来实现这个功能,每次需要比大小的时候就可以调用这个函数,但是使用的前提是你得认识这个单词或者这个函数前面有注释
- C++中支持这样一种机制——运算符重载,我们可以自定义运算符,然后可以同内置类型一样使用这些操作符,比如说我们比较两个日期是否相等,我们可以直接这样操作 int a=d1==d2;这样我们通过返回值就可以判断是否相等,显然大家都认识==这个操作符是用来比较是否相等的
- 所以,运算符重载要比函数调用来的方便,直观的多,在C++中对于这种自定义类型得运算,我们通常都是通过运算符重载来完成的
运算符重载特征
- operator合法的运算符——构成函数名(operator+:重载+运算符)
- 重载运算符以后,不能改变运算符的优先级/结合性/操作数个数
- 五个不能重载的运算符(以、分隔):.* 、 :: 、 sizeof 、 ?: 、 .
基于日期类实现运算符重载
一个基本的日期类
class Date
{
public:
//构造早一个日期对象并对其赋初值,不能有非法值(考虑闰年,2月,负值,大小月等因素)
Date(int year=2018,int month=4,int day=25)
:_year(year)
,_month(month)
,_day(day)
{
if(!IsLegal())
{
assert(false);
}
}
bool IsLegal()
{
return _year > 0
&& _month > 0 && _month <= 12
&& _day > 0 && _day <= GetMonthDay(_year, _month);
}
int GetMonthDay(int year, int month)
{
int days[] = { -1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
int monthday = days[month];
if (month == 2 && IsLeapYear(year))
{
monthday = 29;
}
return monthday;
}
bool IsLeapYear(int year)
{
return year % 4 == 0 && year % 100 != 0
|| year % 400 == 0;
}
Date& operator=(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
return *this;
}
~Date()
{}
private:
int _year;
int _month;
int _day;
}
日期类相关运算的运算符重载
bool operator==(const Date& d)
{
return _year == d._year
&&_month == d._month
&&_day == d._day;
}
bool operator!=(const Date& d)
{
return !(*this == d);
}
bool operator>(const Date& d)
{
return _year > d._year
|| _year == d._year&&_month > d._month
|| _year == d._year&&_month == d._month&&_day > d._day;
}
bool operator<(const Date& d)
{
return !(*this>d && *this == d);
bool operator>=(const Date& d)
{
return (*this > d)||(*this==d);
}
bool operator<=(const Date& d)
{
return !(*this > d);
}
- 在上面的实现中,我们并没有给每一个函数按照它的逻辑一步步的定义,更多的是复用已写的代码
- 注意:不要嵌套调用。大家可以看到我写了一个>,复用了>和==实现了<;<=我是用>实现的,但是>=我并没有用<实现,因为<又会去调用>和==,这样的实现时容易出错的,所以大家在复用的时候应该注意这些问题
Date operator+(int day)
{
if (day<0)
{
return (*this) - (-day);
}
Date& ret(*this);
ret._day += day;
while (ret._day > GetMonthDay(ret._year, ret._month))
{
int monthday = GetMonthDay(ret._year,ret._month);
ret._day -= monthday;
ret._month++;
if (ret._month == 13)
{
ret._year++;
ret._month = 1;
}
}
return ret;
}
Date operator-(int day)
{
if (day < 0)
{
return (*this) + (-day);
}
Date& ret(*this);
ret._day -= day;
while (ret._day <= 0)
{
if (ret._month == 1)
{
ret.GetMonthDay = 12;
--ret._year;
}
else
{
--ret._month;
}
int monthday = GetMonthDay(ret._year, ret._month);
ret._day += monthday;
}
return ret;
}
Date& operator+=(int day)
{
*this = *this + day;
return *this;
Date& operator-=(int day)
{
*this = *this - day;
return *this;
}
- +运算符的重载思想是:若加的天数>0,并且相加后天数超过当月天数,则我们需要向月进位,同时减掉当月天数;如果进位后月份为13了,则向年进位同时置月份为1
- -运算符的重载思想是:若减的天数>0,并且相减的天数小于0,则我们需要向月借位,如果月份减到1了,则需要向年借位,同时置月份为12,年份减1
int operator-(const Date& d)
{
Date& max(*this);
Date min(d);
int flag = 1;
if (*this < d)
{
min = *this;
max = d;
flag = -1;
}
int days = 0;
while (min < max)
{
min++;
days++;
}
return days*flag;
}
- 日期-日期:我们需要考虑的是哪个操作数大,让小的日期往大的日期那边加,这样加的天数就是两个日期之间差的天数。如果我们不比较大小的画,那么减出来的结果可能为正可能为负
Date& operator++()
{
*this += 1;
return *this;
}
Date& operator--()
{
*this -= 1;
return *this;
}
Date operator++(int)
{
Date ret(*this);
*this += 1;
return ret;
}
Date operator--(int)
{
Date ret(*this);
*this -= 1;
return ret;
}
- 前置++和后置++函数名相同,用参数来进行区分,带参的为后置++,这两个运算符构成重载