[C++] 类与对象(中) 一篇带你解决运算符重载实例--日期类Date

少年辛苦终身事,莫向光阴惰寸功。这篇文章主要讲述[C++] 类与对象(中) 一篇带你解决运算符重载实例--日期类Date相关的知识,希望能为你提供帮助。
上上篇文章我们了解了运算符重载,本篇文章我们将通过一个实例:日期类(Date)来巩固运算符重载的知识。
目录
1.全缺省的构造函数
2. operator ==
3.operator <
4.operator < =
5.operator !=
6.operator >
7.operator > = 
8.operator+=(int day) 日期+=天数
9.operator+(int day) 日期+天数
10.operator-=(int day) 日期-=天数
11.operator-(int day) 日期-天数
12.前置++,前置--,后置++,后置--
13.日期-日期
完整代码:
本篇文章我们将主要实现一下接口。

class Date

public:
// 获取某年某月的天数
int GetMonthDay(int year, int month);
// 全缺省的构造函数
Date(int year = 1900, int month = 1, int day = 1);
// 拷贝构造函数
//d2(d1)
Date(const Date& d);
// 赋值运算符重载
// d2 = d3 -> d2.operator=(& d2, d3)
Date& operator=(const Date& d);
// 析构函数
~Date();
// 日期+=天数
Date& operator+=(int day);
// 日期+天数
Date operator+(int day);
// 日期-天数
Date operator-(int day);
// 日期-=天数
Date& operator-=(int day);
// 前置++
Date& operator++();
// 后置++
Date operator++(int);
// 后置--
Date operator--(int);
// 前置--
Date& operator--();
// > 运算符重载
bool operator> (const Date& d);
// ==运算符重载
bool operator==(const Date& d);
// > =运算符重载
inline bool operator > = (const Date& d);
// < 运算符重载
bool operator < (const Date& d);
// < =运算符重载
bool operator < = (const Date& d);
// !=运算符重载
bool operator != (const Date& d);
// 日期-日期 返回天数
int operator-(const Date& d);

private:
int _year;
int _month;
int _day;

;


  此项目为正式的日期类项目,因为我们可以多文件存储。主要包含以下文件:

1.全缺省的构造函数构造函数类似于初始化,其中需要注意的是要对日期的合法性进行判断。由于每个月份的天数存在差别,这里在day中我们需要调用一个GetMonthDay函数接口。
int Date::GetMonthDay(int year, int month)

assert(year > = 0 & & month > 0 & & month < 13);
//静态区开一次
static int monthDayArray[13] =0,31,28,31,30,31,30,31,31,30,31,30,31 ;
if (month == 2 & & isLeapYear(year))

return 29;

return monthDayArray[month];


由于每年有12个月份,每个月的天数存在微小差别,因此我们选择用数组存放对应月份的天数。这里我们月份对应的下标放对应月份的天数,方面调用。由于我们会多次调用此函数,并且我们不会对其中的内容进行更改。因此我们可以将开辟的数组放在静态区。只需要一次初始化可以实现多次调用的功能。提高的程序的效率。 
由于闰年和平年的2月份天数存在差别,因此我们要对2月份这种特殊情况进行判断,如果是闰年就直接返回29,如果是平年就正常返回。
//判断是否闰年
bool isLeapYear(int year)

return (year % 100 != 0 & & year % 4 == 0) || (year % 400 == 0);


Date::Date(int year, int month, int day)

if (year > = 1 & & month < = 12 & & month > = 1 & & day > = 1 & & day < = GetMonthDay(year, month))

_year = year;
_month = month;
_day = day;

else

cout < < "您输入的日期不合法" < < endl;
exit(-1);



2. operator ==要判断两个日期是否相等,需要判断两个日期的年月日分别相等。因此我们需要对三个结果进行逻辑与判断,最终返回布尔结果。
bool Date::operator==(const Date& d)

return _year == d._year & & _month == d._month & & _day == d._day;


3.operator < 要判断日期< 日期。
1、首先要比较对应的年,如果年小后面无需比较,直接返回true.
2、如果年相同,再比较月,如果月小无需比较天,直接返回true.
3、如果年月都相同,此时比较天,如果天小返回true.
因此以上3种情况满足任意一种就返回true,否则返回false.
bool Date::operator < (const Date& d)

if (_year > d._year
|| (_year == d._year & & _month > d._month)
|| (_year == d._year & & _month == d._month & & _day > d._day))

return false;

else

return true;



4.operator < =由于我们已经书写了< 和==,因此如果我们要判断< =我们只需要复用上述写好的两个接口即可。逻辑为< 和==都满足< =。因此< 和==满足其中一个即可返回true,否则返回false.
bool operator < = (const Date& d)

return *this < d || *this == d;


5.operator !=!=和==构成逻辑非,两个接口逻辑相反,因此只需要给==取反即可。
bool operator != (const Date& d)

return !(*this == d);


6.operator > > 和< =构成逻辑非,因此只需要对< =取反即可。
bool operator> (const Date& d)

return !(*this < = d);


7.operator > =  > =和< 构成逻辑非,因此只需要对< 取反即可
bool operator> =(const Date& d)

return !(*this < d);




我们发现,我们只需要实现==和< (或> )两个接口即可实现所有的比较接口,由于复用的接口代码量偏少,使用频繁。因此我们可以将后面复用其他接口的接口置成内敛(inline)函数。因此我们可以写在类中。因为类中默认成员函数为内敛函数。


8.operator+=(int day) 日期+=天数根据逻辑可知,一个日期加上一个天数,将返回一个新的日期。因此这里函数的返回值应为一个日期类Date。
为了实现这个接口,我们可以举例来进行分析:

Date& Date::operator+=(int day)

if (day < 0)
return *this -= -day;
_day += day;
while (_day > GetMonthDay(_year, _month))

_day -= GetMonthDay(_year, _month);
_month++;
if (_month == 13)

++_year;
_month = 1;


return *this;


测试结果:2022-8-27  经过验证正确。

需要注意的是:如果传入的day小于0,我们先对day取相反数再处理即可。 
9.operator+(int day) 日期+天数由于我们已经实现了日期+=天数,因此我们可以复用。+=和+唯一的区别是+=改变本身,+不改变。因此我们创建一个新的Date,存放结果即可。
Date Date:: operator+(int day)

Date ret(*this);
ret += day;

return ret;


测试结果:此时我们发现d1并没有改变,d2改变了。结果正确。

10.operator-=(int day) 日期-=天数逻辑同+=类似,只需要这里注意一些细节节即可,我们同样可以使用一个例子分析
这里如果要是用--,一定是前置--,因此自身是改之后再使用。

需要注意的是:如果传入的day小于0,我们先对day取相反数再处理即可。 
Date& Date::operator-=(int day)

if (day < 0)
return *this += -day;
_day -= day;
while (_day < = 0)

--_month;
if (_month == 0)

_month = 12;
--_year;

_day += GetMonthDay(_year, _month);

return *this;


我们同样使用8月27减去100如果返回5月19说明正确。
测试结果正确

11.operator-(int day) 日期-天数  道理同+=和+的关系相同,同样采用复用。
Date Date::operator-(int day)

Date ret(*this);
ret -= day;

return ret;


测试:

12.前置++,前置--,后置++,后置--


【[C++] 类与对象(中) 一篇带你解决运算符重载实例--日期类Date】我们发现前置++和后置++的操作符都是++操作符,并且他们的返回类型,函数名,参数列表都一模一样。那么他们我们应该怎么区别呢?


这个问题其实编译器也考虑到了,因此编译器为了区别前置++和后置++,编译器规定,后置++(或后置--)的参数列表中多穿一个值。此时因为两个函数的参数列表的参数个数不同,两个函数就构成了函数重载。就可以区分两个操作符了。
需要注意的是,加的这个参数是为了区分前置和后置,因此传过来的值无所谓。我们一般可以省略写就直接只写数据类型。
因此:前置++就相当于是+=1,
Date& operator++()

*this += 1;
return *this;


前置--就相当于-=1
Date& operator--()

*this -= 1;
return *this;


后置++先使用,因此要创建一个tmp存上,再自身++
Date operator++(int)

Date tmp(*this);
*this += 1;
return tmp;


后置--先使用,因此要创建一个tmp存上,再自身--
Date operator--(int)//加参数为了区分,与传过来的值无关

Date tmp(*this);
*this -= 1;
return tmp;


测试:结果正确





注意:由于前置++,前置--,后置++,后置--代码使用次数多,代码篇幅小,因此我们可以写在类内成为内联函数。


13.日期-日期这个接口我们要实现一个日期与另一个日期之间相差多少天。如果是小日期-大日期,应该返回一个负数。因此我们要对这个小细节进行处理。
这里的实现逻辑是:
1、我们先默认第一个日期为大日期,第二个日期为小日期。
2、我们在对这两个进行比较,如果第一个是小日期,我们就将max和min进行日期调换。如果调换说明是小日期-大日期,最终的结果理应该是个负数。因此我们定义一个flag = 1,如果这里调换的话,让flag = -,最终返回n*flag即可。
3、我们要始终保持小追大。我们使用前置++运算,让小的日期追大的日期,这里需要定义一个计数器n。每++一次,n就++一次。
int Date::operator-(const Date& d)

int flag = 1;
Date max = *this; //默认认为第一大
Date min = d;
if (*this < d)

min = *this;
max = d;
flag = -1;

int n = 0;
while (min != max)

++n;
++min;

return n*flag;


测试:结果正确





    推荐阅读