日期类实现运算符重载

一、引言

对于像int、double等这种C++的内置类型来说,加减运算是比较简单的。但是如果想对两个日期类对象进行相减运算,得到两个日期相差多少天又应该怎么操作呢。为此,C++引用了一种叫运算符重载的方法来解决上述问题。

二、简介

  1. 运算符重载是具有特定名字的函数,他的名字是由operator和后面要定义的运算符共同构成。和其他函数一样,他也具有返回类型和参数列表以及函数体。
  2. 重载运算符函数的参数个数和该运算符作用的运算对象数量一样多。一元运算符有一个参数,二元运算符有两个参数,二元运算符的左侧运算对象传给第一个参数,右侧运算对象传给第二个参数
  3. 如果一个重载运算符函数是成员函数,则他的第一个运算对象默认传给隐式的this指针

三、日期类实现运算符重载

头文件用于类成员及其成员函数的声明,代码如下

#pragma once
#include <iostream>
using namespace std;

bool isLeap(int year);
//距离元时间有多少天
int ToOriginDay(int year, int month, int day);

class Date
{
	friend ostream& operator<< (ostream& out, const Date& d);
	friend istream& operator>> (istream& in, Date& d);

public:
	// 获取某年某月的天数
	int GetMonthDay(int year, int month);

	// 全缺省的构造函数
	Date(int year = 1900, int month = 1, int day = 1);

	// 拷贝构造函数
	// d2(d1)
	Date(const Date& d);

	//检查日期是否合法
	bool CheckValue(const Date& d);

	// 赋值运算符重载
	// d2 = d3 -> d2.operator=(&d2, d3)
	Date& operator=(const Date& d);

	// 析构函数
	~Date();

	//打印日期
	void Print();

	// 日期+=天数
	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);

	// >=运算符重载
	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;
};

全缺省构造函数 

缺省构造函数只需在声明或者定义一处标明缺省值即可,否则会出现重定义的情况

// 全缺省的构造函数
Date::Date(int year, int month, int day)
{
	_year = year;
	_month = month;
	_day = day;
}

拷贝构造函数:用于拷贝一个类的成员到另一个类中

// 拷贝构造函数
	// d2(d1)
Date::Date(const Date& d)
{
	_year = d._year;
	_month = d._month;
	_day = d._day;
}

赋值运算符重载:用于把一个类的成员赋值给另一个类的成员,因为返回值为一个该类的类引用,因此 返回该类的this指针指向的类对象,也就是*this

// 赋值运算符重载
// d2 = d3 -> d2.operator=(&d2, d3)
Date& Date::operator=(const Date& d)
{
	_year = d._year;
	_month = d._month;
	_day = d._day;
	return *this;
}

析构函数:将年月日置零

// 析构函数
Date::~Date()
{
	_year = _month = _day = 0;
}

打印日期:

//打印日期
void Date::Print()
{
	cout << _year << "/" << _month << "/" << _day << endl;
}

检查日期是否合法:

//检查日期是否合法
bool Date::CheckValue(const Date& d)
{
	if ((d._month > 0 && d._month < 13) && (d._day > 0 && d._day <= GetMonthDay(d._year, d._month)))
		return true;

	return false;
}

为了后续运算符重载的运算,这里提供三个函数,分别用于判断是否为闰年;传入日期距离0年0月0日(虽然没有0年0月0日,但为了方便计算可以设这个日期)有多少天,这里默认0年0月0日为元时间(当然,你也可以自定义一个元日期例如1900年1月1日,到时候修改i的初始值即可),待会减运算时会用到;获取某年某月有多少天。

bool isLeap(int year)
{
	return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
}

//距离元时间有多少天
int ToOriginDay(int year, int month, int day) {
	int subyear = year - 0;
	int i = 0;
	int days = 0;
	for (i = 0; i < subyear; i++) {
		if (isLeap(i))
			days += 366;
		else
			days += 365;
	}
	int a[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
	if (isLeap(year))
		a[2] = 29;

	for (i = 0; i < month; i++) {
		days += a[i];
	}
	days += day;
	return days;
}

// 获取某年某月的天数
int Date::GetMonthDay(int year, int month)
{
	static int a[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
	if (month == 2 && isLeap(year))
		return 29;

	return a[month];
}

+=和+运算符重载: +=和+运算符重载用于给定一个日期类和一个天数day,求出该日期之后day天是哪一天,但是+=要求原日期改变而+要求原日期不变

对于+=来说,如果是像计算2024年8月17日之后10天这种不涉及跨月的就十分简单了,但是如果要计算100天后甚至1000天后是哪一天还真不好口算。其实,计算这个问题只需解决进位问题即可。我们可以_day先+=day,然后通过循环解决进位。如果现在的_day大于该月最大的天数,证明需要进位到下一个月,则进入循环。进入循环后,先让_day减去距离该月月底的天数,再让月份进一,如果月份为13月则让他重新从1月开始,同年份也要加一年。如此循环,最后返回*this即可

对于+来说,我们可以复用+=运算减轻工作量,定义一个tmp类并把*this给给tmp,让tmp进行+=运算后返回tmp即可

// 日期+=天数
Date& Date::operator+=(int day)
{
	_day += day;
	while (_day > GetMonthDay(_year, _month)) {
		_day -= GetMonthDay(_year, _month);
		++_month;
		if (_month == 13) {
			_month = 1;
			++_year;
		}
	}
	return *this;
}

// 日期+天数
Date Date::operator+(int day)
{
	Date tmp = *this;
	tmp += day;
	return tmp;
}

类似的-=和-运算涉及借位问题,逻辑只是把+=的逻辑倒过来思考

// 日期-=天数
Date& Date::operator-=(int day)
{
	_day -= day;
	while (_day <= 0) {
		--_month;
		if (_month == 0) {
			_month = 12;
			_year--;
		}
		_day += GetMonthDay(_year, _month);
	}
	return *this;
}

// 日期-天数
Date Date::operator-(int day)
{
	Date tmp = *this;
	tmp -= day;
	return tmp;
}

在弄明白+=、+、-=和-后,对于前置++、后置++、前置--、后置--便能触类旁通了,因为他们分别也就是+=一天、+一天、-=一天和-一天而已

// 前置++
Date& Date::operator++()
{
	*this += 1;
	return *this;
}

// 后置++
Date Date::operator++(int)
{
	Date tmp = *this;
	*this += 1;
	return tmp;
}

// 前置--
Date& Date::operator--()
{
	*this -= 1;
	return *this;
}

// 后置--
Date Date::operator--(int)
{
	Date tmp = *this;
	*this -= 1;
	return tmp;
}

对于两个日期相减,用于计算两个日期之间差了多少天,就要用到元日期了。先计算出第一个日期离元日期有多少天,再计算出第二个日期离元日期有多少天,然后分别让这两个天数相减就可以计算出两个日期间相差的天数了。

//距离元时间有多少天
int ToOriginDay(int year, int month, int day) {
	//假设为2024/8/13
	int subyear = year - 0;
	int i = 0;
	int days = 0;
	for (i = 0; i < subyear; i++) {
		if (isLeap(i))
			days += 366;
		else
			days += 365;
	}
	int a[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
	if (isLeap(year))
		a[2] = 29;

	for (i = 0; i < month; i++) {
		days += a[i];
	}
	days += day;
	return days;
}

// 日期-日期 返回天数
int Date::operator-(const Date& d)
{
	int day1 = ToOriginDay(_year, _month, _day);
	int day2 = ToOriginDay(d._year, d._month, d._day);
    if(day1 > day2)
	    return day1 - day2;
    else
        return day2 - day1;
}

日期类可以计算,同样的日期类也可以比较。两个日期之间进行比较就可以知道哪个日期先哪个日期后。

对于>来说,也就是一个日期在另一个日期后面。那么只要年份比你大就大,或者年份相等月份比你大就大,又或者年份月份都相等日期比你大就大。对于==来说就是年月日都一样。对于>=,就可以调用>和=了,只要我大于或者等于你就满足条件。剩下的日期比较逻辑也都类似。

// >运算符重载
bool Date::operator>(const Date& d)
{
	if (_year > d._year)
		return true;
	if (_year == d._year) {
		if (_month > d._month)
			return true;

		if (_month == d._month && _day > d._day)
			return true;
	}

	return false;
}

// ==运算符重载
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 || *this == d;
}

// <运算符重载
bool Date::operator<(const Date& d)
{
	if (_year < d._year)
		return true;
	if (_year == d._year) {
		if (_month < d._month)
			return true;

		if (_month == d._month && _day < d._day)
			return true;
	}

	return false;
}

// <=运算符重载
bool Date::operator <= (const Date& d)
{
	return *this < d || *this == d;
}

// !=运算符重载
bool Date::operator != (const Date& d)
{
	return !(*this == d);
}

对于输入输出流重载,类外实现就要定义成友元函数。分别返回ostream&和istream&即可

//输出流重载
ostream& operator<< (ostream& out, const Date& d)
{
	out << d._year << "/" << d._month << "/" << d._day << endl;
	return out;
}

//检查日期是否合法
bool Date::CheckValue(const Date& d)
{
	if ((d._month > 0 && d._month < 13) && (d._day > 0 && d._day <= GetMonthDay(d._year, d._month)))
		return true;

	return false;
}

//输入流重载
istream& operator>> (istream& in, Date& d)
{
	
	while (1) {
		cout << "请输入合法日期:";
		in >> d._year >> d._month >> d._day;
		
		if (!d.CheckValue(d)) {
			cout << "非法日期" <<endl;
		}
		else {
			break;
		}
	}
	

	return in;
}

 

 

 

 

 

 

 

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值