C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
函数名字为:关键字operator后面接需要重载的运算符符号。
函数原型:返回值类型 operator操作符(参数列表)
下面以日期类为例子
我们知道系统的基本类型可以进行,+、-等基本运算,但是用户的自定义类型,我们不能使用系统的基本类型+和-,需要对其进行函数名重载才能使用这些操作。
运算符既可以是成员函数,也可以是非成员函数。因为我们通常将用户自定义类型的数据定义为私有,在类外不能访问私有成员。所以通常,将运算符重载函数,定义为成员函数。特别注意,成员函数的形参列表,形参的个数比实际的个数少一个。因为有一个是这个类本身。
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cassert>
using namespace std;
class Date
{
public:
Date(int year = 1, int month = 1, int day = 1)
{
if (month >= 13 || month <= 0
|| day >= GetMonthDay(year, month) || day <= 0)
{
assert(false);
}
_year = year;
_month = month;
_day = day;
}
Date& operator=(const Date& d);
bool operator<(const Date& d) const;
bool operator==(const Date& d) const;
bool operator<=(const Date& d) const;
bool operator>=(const Date& d) const;
bool operator>(const Date& d) const;
bool operator!=(const Date& d) const;
Date operator+(int day);
Date& operator+=(int day);
int GetMonthDay(int year, int month);
Date& operator++();
Date operator++(int);
Date& operator-=(int day);
Date operator-(int day);
Date operator--(int);
Date& operator--();
int operator-(const Date& d);
friend ostream& operator<<(ostream& out, const Date& d);
friend istream& operator>>(istream& in, Date& d);
private:
int _year;
int _month;
int _day;
};
=的运算符重载
Date& Date::operator=(const Date& d)
{
if (this != (&d))
{
_year = d._year;
_month = d._month;
_day = d._day;
}
return *this;
}
等号运算符重载的返回值为Date& 类型,使用的是赋值后对象的自引用,可以减少拷贝,提高效率。如果this==&d,说明是自己对自己赋值,不需要进行操作。
==运算符重载
bool Date::operator==(const Date& d) const
{
return _year == d._year && _month == d._month && _day == d._day;
}
==号运算符的重载判断年、月、日是否相等就可以了。
下面就是<、<=、>=、>等运算符的重载
<运算符的重载
bool Date::operator<(const Date& d) const
{
if (_year < d._year)
{
return true;
}
else if (_year == d._year && _month < d._month)
{
return true;
}
else if (_year == d._year && _month == d._month && _day < d._day)
{
return true;
}
return false;
}
<=运算符的重载
在前面,我们已经实现过<、==的运算符重载。只需要复用前面的代码就可以实现<=的运算符重载。
bool Date::operator<=(const Date& d) const
{
return (*this < d) || (*this == d);
}
>运算符的重载
>的反面就是<=,我们对<=取反就可以完成,>运算符的重载。
bool Date::operator>(const Date& d) const
{
return !(*this <= d);
}
>=运算符的重载
>=的反面是<,只需要对<取反就可以完成>=的操作了。
bool Date::operator>=(const Date& d) const
{
return !(*this < d);
}
!=运算符的重载
!=是==的反面
bool Date::operator>(const Date& d) const
{
return !(*this <= d);
}
+=、+、-=、-运算符的重载
+=运算符的重载
int Date::GetMonthDay(int year,int month)
{
int Arry[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
if (month == 2 && (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))
{
return Arry[month] + 1;
}
else
{
return Arry[month];
}
}
Date& Date::operator+=(int day)
{
if (day < 0)
{
return (*this) -= -day;
}
else
{
_day += day;
while (_day > GetMonthDay(_year, _month))
{
_day -= GetMonthDay(_year, _month);
++_month;
if (_month == 13)
{
_month = 1;
++_year;
}
}
return *this;
}
}
如果一个类+=day,我们先将这个类的_day+day,再判断_day的天数,有没有大于这个月的天数,如果大于了,类的月份+1,月份+1后判断,月份是否超过了12,超过了的话将月份置为1,年份+1.循环往复,就可以就出+day后的日期了。
如果+=一个负数的天数,实际上是-=一个整数的天数,我们调用-=就好了。
+运算符的重载
+运算符和+=运算符类似。
比如: int a=3;
a+3返回结果6,但是a还是3
a+=3,返回6,a变成了6
Date Date::operator+(int day)
{
Date tmp = (*this);
tmp += day;
return tmp;
}
-=运算符的重载
Date& Date::operator-=(int day)
{
if (day < 0)
{
return (*this) += -day;
}
else
{
_day -= day;
while (_day <= 0)
{
--_month;
if (_month == 0)
{
_month = 12;
--_year;
}
_day += GetMonthDay(_year, _month);
}
return *this;
}
}
-=运算符的操作和+=的类似,我们先把类的_day-=day;如果得到一个负数,说明日期的_day不足以-day,我们需要向月份借天数,将月份--,然后_day+=借的那个月的天数。月份--之后,我们需要对月份判断是否正确,如果本来月份是1,--变成0,实际上是去年的12月,需要将_month=12,还有减少年份。之后就需要循环判断,直到_day>0为止。
-运算符的重载
-和-=运算符类似
Date Date::operator-(int day)
{
Date tmp = (*this);
tmp -= day;
return tmp;
}
++、--运算符的重载
++运算符的重载
++分为前置++与后置++
前置++运算符的重载
前置++返回的是++后的结果
Date& Date::operator++()
{
*this = *this + 1;
return *this;
}
后置++的运算符重载
我们怎么区分前置和后置呢,在形参列表加入一个int就可以说明这个是后置++了。这个int便于编译器区分。
后置++返回的是++之前的结果,++的数据仍然就行了自增。
Date Date::operator++(int)
{
Date tmp = (*this);
*this =(*this)+1;
return tmp;
}
前置--运算符重载函数
Date& Date::operator--()
{
(*this) -= 1;
return *this;
}
后置--运算符重载函数
Date Date::operator--(int)
{
Date tmp = (*this);
*this -= 1;
return tmp;
}
那么我们如何计算两个日期相差多少天呢。利用日期-时期。所以需要我们构造日期-日期的运算符重载函数。
int Date::operator-(const Date& d)
{
Date max=(*this);
Date min=d;
int flag = 1;
if (*this < d)
{
max = d;
min = (*this);
flag = -1;
}
int n = 0;
while (min != max)
{
min++;
n++;
}
return flag*n;
}
两个日期相减,我们需要先找到两个中较大的日期。
小的日期自增到与大的日期相等,自增的次数就是两个日期相差的天数。加一个flag标志用来区分日期相差的正负。
<<流插入运算符重载。我们知道流插入运算符可以打印基本数据类型。用户自定义的类型,能不能使用系统自带的流插入运算符呢?是不行的,需要我们进行函数名重载,便于支持自定义类型的打印。
ostream& operator<<(ostream& out,const Date& d)
{
out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
return out;
}
流插入运算符的重载函数。不能作为类的成员函数,因为成员函数的第一个参数默认是类的this指针。但是我们使用流插入运算符是cout<<d<<endl;第一个参数为cout,所以我们不能使用成员函数作为重载函数,那么我们类外不能访问私有成员。该怎么办?我们将函数声明为友元函数,友元函数可以在类外访问私有成员。
在类内加入
为什么返回类型为ostream呢,因为这样可以做到连续的流插入,输出多个结果。
流提取运算符重载函数
我们知道>>可以输入基本的数据类型。但是日期类是我们自定义的类型。我们使用>>输入就需要对>>进行运算符重载。
与流插入运算符的重载函数一样。流提取运算符重载函数,也不能使用成员函数。写在类外将这个函数作为类的友元函数。
istream& operator>>(istream& in, Date& d)
{
in >> d._year >> d._month >> d._day;
return in;
}
在类内声明:
对<<、>>进行了运算符重载。我们就可以直接使用其操作了。