我们要实现日期的加减、大小比较等功能。

一、声明

这里先创建一个头文件来进行定义声明:

Date(int year = 1900, int month = 1, int day = 1);
void Print();

创建Date对象时初始化日期(年、月、日)

void Print();作为普通成员函数,用于日期打印。

	int GetMonthDay(int year, int month)
	{
		assert(month > 0 && month < 13);
		static int monthDayArray[13] = { -1, 31, 28, 31, 30, 31, 30,//0月为-1,没有
31, 31, 30, 31, 30, 31 };
		if (month == 2 && (year % 4 == 0 && year % 100 != 0) || (year
			% 400 == 0))
		{
			return 29;
		}
		else
		{
			return monthDayArray[month];
		}
		return monthDayArray[month];
	}
	bool CheckDate();

我们创建一个月份函数GetMonthDay,用于判断每个月固定的天数是多少,并且判断是否为润年。在这因为要频繁调用月份函数,所以建议使用静态数组减少开销。

CheckDate是用于判断,输入的日期是否有误。

//声明定义分离
bool operator<(const Date& d);
bool operator<=(const Date& d);
bool operator>(const Date& d);
bool operator>=(const Date& d);
bool operator==(const Date& d);
bool operator!=(const Date& d);
Date& operator+=(int day);
Date operator+(int day);
Date& operator-=(int day);
Date operator-(int day);
Date& operator++();
Date operator++(int);
int operator-(const Date& d);

上面这些就是我们要实现的功能,operator 作为专门用于重载运算符的关键字,我们在这给她先声明。

private:
	int _year;
	int _day;
	int _month;
};

这里就是声明我们的年月日了。

//友元函数声明后可以访问私有函数:
friend ostream& operator<<(ostream& out, const Date& d);
friend istream& operator>>(istream& in, Date& d);
ostream& operator<<(ostream& out, const Date& d);
istream& operator>>(istream& in, Date& d);

上面是流的重载方便我们进行日期输入和输出,friend 是友元函数的定义,使用友元函数可以访问私有成员函数。

值得注意的是,流的重写不能放在类中,否则会因为内置的隐藏参数this指针导致会以奇怪的方式实现原功能。

下面就是完整的头文件Date.h:

#pragma once
#include
using namespace std;
#include
class Date
{
	//友元函数声明后可以访问私有函数:
	friend ostream& operator<<(ostream& out, const Date& d);
	friend istream& operator>>(istream& in, Date& d);
public:
	Date(int year = 1900, int month = 1, int day = 1);
	void Print();
	//获取某一年某一天
	//GetMonthDay没有声明和定义分离,默认是inline函数,对于这种小而频繁调用的函数设为内联函数减少开销
	int GetMonthDay(int year, int month)
	{
		assert(month > 0 && month < 13);
		static int monthDayArray[13] = { -1, 31, 28, 31, 30, 31, 30,//0月为-1,没有
31, 31, 30, 31, 30, 31 };//因为频繁调用月分,直接放入静态区节省开销
		//闰年判断
		if (month == 2 && (year % 4 == 0 && year % 100 != 0) || (year
			% 400 == 0))
		{
			return 29;
		}
		else
		{
			return monthDayArray[month];
		}
		return monthDayArray[month];
	}
	bool CheckDate();//判断是否输入错误日期
	//声明定义分离
	bool operator<(const Date& d);
	bool operator<=(const Date& d);
	bool operator>(const Date& d);
	bool operator>=(const Date& d);
	bool operator==(const Date& d);
	bool operator!=(const Date& d);
	Date& operator+=(int day);
	Date operator+(int day);
	Date& operator-=(int day);
	Date operator-(int day);
	Date& operator++();
	Date operator++(int);
	//这里有++d1和d1++两种形态,所以规定实现++重载的是前置++d1,后置++重载需要添加一个int形参,入d1.operator(0)
	int operator-(const Date& d);
private:
	//在重载流要使用这三个得设为全局函数,或者增加三个公有函数
	//或者用另一种方法,友元元声明
	int _year;
	int _day;
	int _month;
};
//流重载
//流重载若放在成员函数里,会不可避免的出现左右操作数要按顺序对齐,导致原本cout<>(istream& in, Date& d);

二、实现

void Date::Print()
{
	cout << _year << "-" << _month << "-" << _day << endl;
}

函数打印,输出我们输入的日期。

bool Date::CheckDate()
{
	if (_month < 1 || _month > 12
		|| _day < 1 || _day > GetMonthDay(_year, _month))
		//不属于一到十二月,和1到月底日数
	{
		return false;
	}
	else
	{
		return true;
	}
}
Date::Date(int year, int month, int day)
{
	_year = year;
	_month = month;
	_day = day;
	if (!CheckDate()) {
		cout << "非法日期:" << endl;
		Print();
	}
}

实现我们的年份判断,当月份和天数不在GetMonthDay规定的对应月份所拥有的天数中和月份不在1-12月时,返回false,并且打印出错误日期。

bool Date:: operator < (const Date& d) {
	if (_year < d._year) {
		return true;//比较年
	}
	else if (_year == d._year) {//年相等比较月
		if (_month < d._month) {
		return true;
		}
		else if (_month == d._month) {
			if (_day < d._day) {
				return true;
			}
		}
		else {
			return false ;
		}
	}
}

我们开始实现小于比较,通过逐步对比年月日来进行判断。

bool Date:: operator <= (const Date& d) {
	return *this < d || *this == d;
	}
 bool Date:: operator >= (const Date& d) {
	return *this > d || *this == d;
	}
bool Date:: operator > (const Date& d) {
	//return *this < d || *this == d;
	return !(*this <= d);
	}
bool Date:: operator == (const Date& d) {
	return _year == d._year && _month == d._month && _day & d._day;
}
bool Date:: operator != (const Date& d) {
	return  !(*this == d);
}

这里可以使用复用简化代码来实现他们的功能,但要注意的是必须有一个运算符按照<符的方式重写,否则会调入无线调用的死循环里。

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)//如果出现13月
		{
			++_year;//进入下一年,月份归为一月
			_month = 1;
		}
	}
	return *this;
}
Date Date::operator+(int day)
{
	Date tmp = *this;
	tmp += day;//函数复用,不用费那么多脑细胞再写一个逻辑
	//tmp._day += day;//+不能改变自身值
	//while (tmp._day > GetMonthDay(tmp._year, tmp._month))//如果天数大于当前年月的天数
	//{
	//	tmp._day -= GetMonthDay(tmp._year, tmp._month);//当前天数减去当前月份的最大天数
	//	++tmp._month;//进入下一个月
	//	if (tmp._month == 13)//如果出现13月
	//	{
	//		++tmp._year;//进入下一年,月份归为一月
	//		tmp._month = 1;
	//	}
	//}
	return tmp;
}
Date Date::operator-(int day)
{
	Date tmp = *this;
	tmp -= day;
	return tmp;
	////-=复用-
	// Date tmp = *this;
	//if (day < 0)//获取需要减少的天数,入-50天
	//{
	//	return *this += -day;
	//}
	//tmp._day -= day;//当前天数减去需要减去的天数
	//while (tmp._day <= 0)//剩余天数小于0,退回上一个月,月数不足退回去年的12月,加上回退月数所有的天数,继续减去剩余天数,直到为正。
	//{
	//	--tmp._month;
	//	if (tmp._month == 0)
	//	{
	//		tmp._month = 12;
	//		tmp._year--;
	//	}
	//	// 借上⼀个⽉的天数
	//	tmp._day += GetMonthDay(tmp._year, tmp._month);
	// return tmp;一次拷贝
	//}
}
Date& Date::operator-=(int day)
{
	if (day < 0)//获取需要减少的天数,入-50天,则转换为正
	{
		return *this += -day;
	}
	_day -= day;//当前天数减去需要减去的天数
	while (_day <= 0)//剩余天数小于0,退回上一个月,月数不足退回去年的12月,加上回退月数所有的天数,继续减去剩余天数,直到为正。
	{
		--_month;
		if (_month == 0)
		{
			_month = 12;
			_year--;
		}
		// 借上⼀个⽉的天数
		_day += GetMonthDay(_year, _month);
	}
	//-=复用-,相比-复用-=这里会多拷贝三次,增加开销.前两次是调用operator-=时两次拷贝和一次赋值拷贝
	//*this = *this - day;
	//相当于*this -= day;
	return *this;
}

这里实现了+、+=、-、-=四个运算符的重载,值得注意的是,在-和-=重写我注释掉了另外一种重写方法,两种方法是谁先重写就先复用谁,但是第二种方法会花费更大的开销。

//d1++ ;括号里数字随意,是整数就行
Date Date::operator++(int) {
	Date tmp = *this;
	*this += 1;
	return tmp;
}
//++d1
Date& Date::operator++() {
	*this += 1;
	return *this;
}

然后++的重载有些特殊,因为++分为++d和d++,在C++里规定,d++这种后置写法,重写需要带上参数,否则调用的是++d的重写方法。

//日期相减
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 n * flag;
}

这里我们传入两个日期,当max日期小于min日期时进行交换,揉捏在进行循环增加小的日期,直到两日期相等,我们则将重复增加多少次的次数返回,得到的就是两日期相差天数。flag是用于判断起始日期差是增加还是减少。

ostream& operator<<(ostream& out, const Date& d)
{
	out << d._year << "年" << d._month << "⽉" << d._day << "⽇" << endl;
	return out;
}
istream& operator>>(istream& in, Date& d)
{
	while (1) {
		cout << "请依次输⼊年⽉⽇:>";
		in >> d._year >> d._month >> d._day;
		if (!d.CheckDate())
		{
			cout << "⽇期⾮法" << endl;
			d.Print();
			cout << "重新输入" << endl;
		}
	}
	return in;
}

这里就是我们重写的输入输出流,方便我们直接输入年月日 并且以更直观的方式输出。

Date.cpp:

#include"Date.h"
bool Date::CheckDate()
{
	if (_month < 1 || _month > 12
		|| _day < 1 || _day > GetMonthDay(_year, _month))
		//不属于一到十二月,和1到月底日数
	{
		return false;
	}
	else
	{
		return true;
	}
}
Date::Date(int year, int month, int day)
{
	_year = year;
	_month = month;
	_day = day;
	if (!CheckDate()) {
		cout << "非法日期:" << endl;
		Print();
	}
}
void Date::Print()
{
	cout << _year << "-" << _month << "-" << _day << endl;
}
bool Date:: operator < (const Date& d) {
	if (_year < d._year) {
		return true;//比较年
	}
	else if (_year == d._year) {//年相等比较月
		if (_month < d._month) {
		return true;
		}
		else if (_month == d._month) {
			if (_day < d._day) {
				return true;
			}
		}
		else {
			return false ;
		}
	}
	/*return !(*this>=d);*///这里不用下面方式是因为至少有一个上述比较,否则会出现无限调用·死循环
}
//以此类推
bool Date:: operator <= (const Date& d) {
	return *this < d || *this == d;
	}
 bool Date:: operator >= (const Date& d) {
	return *this > d || *this == d;
	}
bool Date:: operator > (const Date& d) {
	//return *this < d || *this == d;
	return !(*this <= d);
	}
bool Date:: operator == (const Date& d) {
	return _year == d._year && _month == d._month && _day & d._day;//等价上面的判断,这里不用和下面一样的方式是会导致无线递归
}
bool Date:: operator != (const Date& d) {
	return  !(*this == d);
}
//定义
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)//如果出现13月
		{
			++_year;//进入下一年,月份归为一月
			_month = 1;
		}
	}
	return *this;
}
Date Date::operator+(int day)
{	//+可以使用拷贝构造
	Date tmp = *this;
	tmp += day;//函数复用,不用费那么多脑细胞再写一个逻辑
	//tmp._day += day;//+不能改变自身值
	//while (tmp._day > GetMonthDay(tmp._year, tmp._month))//如果天数大于当前年月的天数
	//{
	//	tmp._day -= GetMonthDay(tmp._year, tmp._month);//当前天数减去当前月份的最大天数
	//	++tmp._month;//进入下一个月
	//	if (tmp._month == 13)//如果出现13月
	//	{
	//		++tmp._year;//进入下一年,月份归为一月
	//		tmp._month = 1;
	//	}
	//}
	return tmp;
}
Date Date::operator-(int day)
{
	Date tmp = *this; //一次拷贝
	tmp -= day;
	return tmp;//一次拷贝
	////-=复用-
	// Date tmp = *this;
	//if (day < 0)//获取需要减少的天数,入-50天
	//{
	//	return *this += -day;
	//}
	//tmp._day -= day;//当前天数减去需要减去的天数
	//while (tmp._day <= 0)//剩余天数小于0,退回上一个月,月数不足退回去年的12月,加上回退月数所有的天数,继续减去剩余天数,直到为正。
	//{
	//	--tmp._month;
	//	if (tmp._month == 0)
	//	{
	//		tmp._month = 12;
	//		tmp._year--;
	//	}
	//	// 借上⼀个⽉的天数
	//	tmp._day += GetMonthDay(tmp._year, tmp._month);
	// return tmp;一次拷贝
	//}
}
Date& Date::operator-=(int day)
{
	if (day < 0)//获取需要减少的天数,入-50天,则转换为正
	{
		return *this += -day;
	}
	_day -= day;//当前天数减去需要减去的天数
	while (_day <= 0)//剩余天数小于0,退回上一个月,月数不足退回去年的12月,加上回退月数所有的天数,继续减去剩余天数,直到为正。
	{
		--_month;
		if (_month == 0)
		{
			_month = 12;
			_year--;
		}
		// 借上⼀个⽉的天数
		_day += GetMonthDay(_year, _month);
	}
	//-=复用-,相比-复用-=这里会多拷贝三次,增加开销.前两次是调用operator-=时两次拷贝和一次赋值拷贝
	//*this = *this - day;
	//相当于*this -= day;
	return *this;
}
	//d1++ ;括号里数字随意,是整数就行
	Date Date::operator++(int) {
		Date tmp = *this;
		*this += 1;
		return tmp;
	}
	//++d1
	Date& Date::operator++() {
		*this += 1;
		return *this;
	}
	//前置后置推荐使用前置,后置多两次拷贝
	//日期相减
	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 n * flag;
	}
	ostream& operator<<(ostream& out, const Date& d)
	{
		out << d._year << "年" << d._month << "⽉" << d._day << "⽇" << endl;
		return out;
	}
	istream& operator>>(istream& in, Date& d)
	{
		while (1) {
			cout << "请依次输⼊年⽉⽇:>";
			in >> d._year >> d._month >> d._day;
			if (!d.CheckDate())
			{
				cout << "⽇期⾮法" << endl;
				d.Print();
				cout << "重新输入" << endl;
			}
		}
		return in;
	}

posted on 2025-10-24 14:22  lxjshuju  阅读(6)  评论(0)    收藏  举报