对于内置类型,编译器可以帮助我们完成各种操作符的计算,对于自定义类型却无法直接完成计算,因为自定义类型在没有规定时编译器不知道如何计算。
比如日期类比较大小,我们都知道先比年份,年份大的日期大,年份相同比月份,最后在比天。
虽然我们知道这样比较,但编译器压根就不知道这样比较。
由于是自定义类型,我们想要完成这样的比较只能自己去定义一些运算符,这样自定义运算符的方式叫运算符重载。
那是不是对于所有的类型都可以使用运算符重载?
当然不行。如果对于内置类型也能运算符重载,也就是说连基本的加减乘除都可以重新定义,那所有的计算岂不是乱套了,所以内置类型不支持运算符重载。
如何使用运算符重载呢?
函数名字为:关键字operator后面接需要重载的运算符符号。下面来实现日期类大于的比较:
bool operator>(Date d)
{
if (this->_year > d._year)
return true;
else if (this->_year == d._year&&this->_month > d._month)
return true;
else if (this->_year == d._year&&this->_month == d._month&&this->_day > d._day)
return true;
else
return false;
}
可是,这不是两个类之间比较大小嘛,怎么就只有一个形参了呢?
其实作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this。显然,这里的this指针指向的是左操作数,而形参d是右操作数。
对于日期类这种都是int类型的成员,即使我们不写赋值拷贝,编译器也能完成值拷贝,不妨将赋值操作符补充完整:
Date&operator=(const Date d)
{
this->_year = d._year;
this->_month = d._month;
this->_day = d._day;
return *this;
}
这里返回的是this指针指向的内容,传进来是this指向的内容,返回的也是它,故这里应该用引用返回。
判断两个日期类相等必须是年份相等,月份相等,天也相等,接下来实现相等比较:
bool operator==(Date d)
{
if (this->_year == d._year&&this->_month == d._month&&this->_day == d._day)
return true;
return false;
}
只要不是大于,那肯定就是小于等于,我们判断小于等于就可以借助上面写过的大于比较,如果是大于就一定不是小于等于,返回假,否则就一定是真。
bool operator<=(Date d)
{
if (*this > d)
return false;
return true;
}
有了以上这些操作符,我们很容易用类似的方法写出小于和大于等于:
bool operator<(Date d)
{
if (*this > d || *this == d)
return false;
return true;
}
bool operator>=(Date d)
{
if (*this < d)
return false;
return true;
}
以上就是所有的日期类大小比较。
如果想自定义加减法呢(日期类的乘除法没有意义)
对于加法,我们应该是期望某个日期和天相加(日期类+日期类没有意义)加法的运算规则应该是,满天进月,满月进年。一年12个月好说,可是一个月有多少天是不确定的,而且还需要考虑闰年,所以有必要写一个函数来获取天数,当知道了这个月的天数就可以完成进位,从而实现相加。
int Days(int y, int m)
{
int arr[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
if (y == 2 && y % 4 == 0 && y % 100 != 0 || y % 400 == 0)
return 29;
return arr[m];
}
Date operator+(int n)
{
Date tmp = *this;
tmp._day += n;
while (tmp._day > Days(tmp._year, tmp._month))
{
tmp._day -= Days(tmp._year, tmp._month);
tmp._month++;
}
while (tmp._month > 12)
{
tmp._month -= 12;
tmp._year++;
}
return tmp;
}
和内置类型的加法类似,我们不希望改变原来的值,而是返回一个加了之后的值,所以这里得创建临时的Date类,最后返回这个临时变量,而不是返回this
对于减法,既可以认为是日期类-日期类,也可以是日期类-天数,两种都有意义.
可是都是减法编译器能正确调用哪个运算符重载吗?
要知道,编译器会自动匹配参数类型最符合的运算符重载,从而完成我们想要的计算
Date operator-(int n)
{
Date tmp = *this;
tmp._day -= n;
while (tmp._day <=0)
{
tmp._day += Days(tmp._year, tmp._month);
tmp._month--;
}
while (tmp._month <=0)
{
tmp._month += 12;
tmp._year--;
}
return tmp;
}
int operator-(const Date d)
{
if (*this < d)
{
assert(false);
return -1;
}
Date tmp = d;
int n = 0;
while (!(tmp == *this))
{
++tmp;
n++;
}
return n;
}
同理,我们也可以完成+=,++等操作。
Date&operator++()
{
*this=*this + 1;
return *this;
}
Date&operator--()
{
*this = *this - 1;
return *this;
}
Date&operator+=(int n)
{
*this = *this + n;
return *this;
}
Date&operator-=(int n)
{
*this = *this - n;
return *this;
}
这里的++和--操作是前置++和前置--,如果我们想要的是后置++或后置--呢,operator后面的符号已经完全相同该如何进行区分?
祖师爷进行区分的方式是,将后置++或--里面的参数加上int,这样参数类型和前置++或--操作的不同,使其构成重载。
这样一来,后置++和后置--就能实现了:
Date operator++(int)
{
Date tmp = *this;
*this = *this + 1;
return tmp;
}
Date operator--(int)
{
Date tmp = *this;
*this = *this - 1;
return tmp;
}
至此,一个日期类的基本运算符就实现了。