日期类的实现

89 阅读1分钟

「这是我参与2022首次更文挑战的第21天,活动详情查看:2022首次更文挑战

这篇文章会更深入的学习常见的运算符重载,当然还有一些需要后面的知识铺垫,比如流提取、流插入运算符。

💨Date.h

#pragma once
#include<iostream>
#include<assert.h>
#include<stdbool.h>
using namespace std;
class Date
{
public:
	//获取某年某月的天数
	int GetMonthDay(int year, int month)
	{
		assert(month > 0 && month < 13);
		//默认平年
		int monthDays[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 29;
		}
		return monthDays[month];
	}
	Date(int year = 0, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
		//判断日期是否合法
		if (_year < 0 || _month <= 0 || _month >= 13 || _day <= 0 || _day > GetMonthDay(_year, _month))
		{
			cout << _year << "/" << _month << "/" << _day << "->";
			cout << "非法日期" << endl;
		}
	}
	void Print() const
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}
	bool 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;
		else
			return false;
	}
	bool operator==(const Date& d) const
	{
		return _year == d._year && _month == d._month && _day == d._day;
	}
	bool operator>=(const Date& d) const
	{
		return *this > d || *this == d;//复用operator>、operator==
	}
	bool operator<(const Date& d) const
	{
		return !(*this >= d);//复用operator>=,再取反
	}
	bool operator<=(const Date& d) const
	{
		return !(*this > d);//复用operator>,再取反
	}
	bool operator!=(const Date& d) const
	{
		return !(*this == d);//复用operator==,再取反	
	}

	Date operator+(int day) const;	
	Date operator-(int day) const;
	Date& operator+=(int day);
	Date& operator-=(int day);
	Date& operator++();
	Date operator++(int);
	Date& operator--();
	Date operator--(int);
	int operator-(const Date& d) const;
private:
	int _year;
	int _month;
	int _day;
};

💨Date.cpp

#include"Date.h"

Date Date::operator+(int day) const
{
	//Date temp(*this);
	//temp._day += day;
	//while (temp._day > GetMonthDay(temp._year, temp._month))
	//{
	//	temp._day -= GetMonthDay(temp._year,temp._month);
	//	++temp._month;
	//	if (temp._month == 13)
	//	{
	//		++temp._year;
	//		temp._month = 1;
	//	}
	//}
	//return temp;

	//相同逻辑太多,直接复用operator+= (这个比较好)
	Date temp = *this;
	temp += day;
	return temp;
}
Date& Date::operator+=(int day)
{
	if (day < 0)//d1 + -100;
	{
		return *this -= -day;//+= -100到-= 100
	}
	_day += day;
	//日期不合法,需要进位
	while (_day > GetMonthDay(_year, _month))
	{
		_day -= GetMonthDay(_year, _month);
		++_month;
		if (_month == 13)
		{
			++_year;
			_month = 1;
		}
	}
	return *this;

	//相同逻辑太多,直接复用operator+
	//*this = *this + day;
	//return *this;
}
Date Date::operator-(int day) const
{
	Date temp = (*this);
	temp -= day;
	return temp;
}
Date& Date::operator-=(int day)
{
	if (day < 0)
	{
		return *this += -day;
	}
	_day -= day;
	while (_day < 1)
	{
		--_month;
		if (_month == 0)
		{
			--_year;
			_month = 12;
		}
		_day += GetMonthDay(_year, _month);
	}
	return *this;
}
Date& Date::operator++()
{
	*this += 1;//复用operator+=
	return *this;//返回++后的值
}
Date Date::operator++(int)
{
	Date temp = *this;
	*this += 1;//复用operator+=
	return temp;//返回++前的值
}
Date& Date::operator--()
{
	*this -= 1;//复用operator-=
	return *this;//返回--后的值
}
Date Date::operator--(int)
{
	Date temp(*this);
	*this -= 1;//复用operator-=
	return temp;//返回--前的值
}
int Date::operator-(const Date& d) const
{
	//比较大小
	Date max = *this, min = d;
	int flag = 1;
	if (*this < d)
	{
		max = d;
		min = *this;
		flag = -1;
	}

	int n = 0;
	while (min != max)
	{
		++min;//利用operator++()
		++n;
	}
	return n * flag;
}

💨Test.cpp

#include"Date.h"

void TestDate1()
{
	Date d1(2021, 10, 11);//ok
	Date d2(2021, 2, 29);//err
	Date d3(2020, 2, 29);//ok
	Date d4(2020, 13, 29);//err
}
void TestDate2()
{
	Date d1(2021, 10, 11);
	Date ret;

	ret = d1 + 100;
	ret.Print();

	ret = d1 += 100;
	ret.Print();
	d1.Print();

	ret = d1 + -100;
	ret.Print();
}
void TestDate3()
{
	Date d1(2021, 10, 11);
	Date ret;

	ret = d1 - 11;
	ret.Print();

	ret = d1 -= 11;
	ret.Print();
	d1.Print();

	ret = d1 - -11;
	ret.Print();
}
void TestDate4()
{
	Date d1(2021, 10, 11);
	Date ret;

	ret = ++d1;
	ret.Print();
	d1.Print();

	ret = d1++;
	ret.Print();
	d1.Print();
}
void TestDate5()
{
	Date d1(2021, 10, 11);
	Date ret;

	ret = --d1;
	ret.Print();
	d1.Print();

	ret = d1--;
	ret.Print();
	d1.Print();
}
void TestDate6()
{
	Date d1(2023, 10, 11);
	Date d2(2022, 10, 11);

	cout << d1 - d2 << endl;
	cout << d2 - d1 << endl;
}
int main()
{
	//TestDate1();//日期合法
	//TestDate2();//+、+=、+ -(负)
	//TestDate3();//-、-=、- -(负)
	//TestDate4();//++x、x++
	//TestDate5();//--x、x--
	TestDate6();//d1 - d2
	return 0;
}

📝说明

日期的不合法,构造函数有无责任 ❓

有的,构造函数不仅仅是完成初始化工作,还要判断数据是否合法。

除了上面说的 5 个运算符不能重载之外,其余的运算符有重载的意义 ❓

一个类到底要重载哪些运算符,是看你需要哪些运算符,并且要考量重载的运算符有无价值。

运算符重载 | 函数重载 ❓

虽然它们都用了重载这个词,但是它们之间没有关联。

operator+ | operator+= ❓

对于大量重复的代码我们可以利用复用来提高代码质量 (它俩必须得实现一个)。 在这里插入图片描述 operator+复用operator+=不需要声明 ❓

operator+ 是在 operator+= 之前,所以需要声明,但是我们的 Date.cpp 文件中的第一行,已经把 Date.h 的声明都展开了,所以这里的先后位置可随意。

Date d3 = d1 + -100 & Date d3 = d1 - -100 ❓

显然我们并未考虑到这种情况,所以这里我们还可以把复用用到极致。 在这里插入图片描述 运算符前置++和后置++怎么进行重载(同前置- -和后置- -) ❓

它们的共同点都是需要 ++ 的,但是它们的返回值不同:前置++ 的返回值是 ++ 以后的;后置++ 的返回值是 ++ 之前的。

这两个运算符都是单操作数,也就是只能有一个 this 指针,按正常的运算符重载规则是无法区分前置与后置的,所以编译器为了能区分这种情况,这里就做了一个特殊的处理 —— 给后置++ 增加一个 int 参数 (这个参数仅仅是为了区分,你给多少它都不会用),这样它们就能构成函数重载。

日期 - 日期 ❓

常规思路是日减、月减、年减,补它们中间的位,但是实际上实现较难。

思路一:把某月某日映射到 365 天

思路二 (最优):先比较两日期的大小,然后让小的++,当小的等于大的时,那么加了多少次就是它们相差的天数

比较运算符重载 ❓

常规思路是写好一个比较运算符重载之后就复制代码改符号。

利用复用的思路是实现 operator> 和 operator== 就行了。