手把手教你实现日期类

目录

前言

1.头文件的实现

2.日期类函数各项功能实现

2.1 初始化和打印(比较简单)

2.2日期大小判断

2.3日期的加减运算

3.日期类的输入输出

4.测试代码参考

结束语



前言

前面我们讲解了类的对象的大部分知识,例如拷贝构造,函数重载等知识,本节我们将用所学的知识对日期类进行实现。

注意:本篇博客代码的实现综合了前面所学,同时可能会捎带一点点后面所学内容。

1.头文件的实现

定义日期的类,对公共部分的函数进行声明,私有成员的确定。

函数有日期输入输出,日期判断,日期的大小比较,日期增减。

#pragma once
#include <iostream>
#include <assert.h>
using namespace std;
class Date {
	friend ostream& operator<<(ostream& out, const Date&d);
	friend istream& operator>>(istream& in, Date& d);
public:
	Date(int year = 1, int month = 1, int day = 1);
	void Print();
	int Getday(int year, int month) const{
		assert(month > 0 && month < 13);
		static int monthDayArray[13] = { -1, 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;
		}
		else
			return monthDayArray[month];
	}

	bool CheckDate();
	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;
	// d1 += 天数
	Date& operator+=(int day);
	Date operator+(int day) const;
	// d1 -= 天数
	Date& operator-=(int day);
	Date operator-(int day) const;
	// d1 - d2
	int operator-(const Date& d) const;
	
	// ++d1 -> d1.operator++()
	Date& operator++();
	
	// d1++ -> d1.operator++(0)
	// 为了区分,构成重载,给后置++,强⾏增加了⼀个int形参
	// 这⾥不需要写形参名,因为接收值是多少不重要,也不需要⽤
	// 这个参数仅仅是为了跟前置++构成重载区分
	Date operator++(int);
	Date& operator--();
	Date operator--(int);
	// 流插⼊,流输入输出
  // 不建议,因为Date* this占据了⼀个参数位置,使⽤d<<cout不符合习惯
   //void operator<<(ostream& out);

private:
	int _year;
	int _month;
	int _day;
};
ostream& operator<<(ostream& out, const Date&d);
istream& operator>>(istream& in, Date& d);

在这里,我们用了一个数组来存取各月份的天数,避免连续用多个if else选择语句使代码冗杂,痛过闰年判断来进一步决定二月份的天数。

这个获取月份天数我们要频繁调用,所以我们直接定义在类里面,默认内联函数,频繁调用。

2.日期类函数各项功能实现

因为是在.cpp文件,在我们定义的头文件Date类以外,所以类外声明我们要用到 ::  操作符。

即Date:: 

2.1 初始化和打印(比较简单)

bool Date::CheckDate() {
	if (_month < 1 || _month > 12
		|| _day < 1 || _day > Getday(_year, _month))
	{
		return false;
	}
	else
	{
		return true;
	}
}
Date::Date(int year, int month, int day)
{
	_year = year;
	_month = month;
	_day = day;
	if (!CheckDate())
	{
		cout << "⽇期⾮法" << endl;
	}
}
void Date::Print(){
	cout << _year << "-" << _month << "-" << _day << endl;
}

这里我们写了个检查日期函数,为了方便判断我们所给定的日期是否是非法的,也使日期类函数功能更加完善。

2.2日期大小判断

判断日期大小,从年月日依次判断,通过代码发现,只要实现了基本的等于和大小判断,其他的判断实现我们都可以用过逻辑运算形式来实现。这样可以减少代码量。

bool Date::operator<(const Date& d) const {
	if (_year < d._year)
		return true;
	else if (_year == d._year) {
		if (_month < d._month)
			return true;
		else if (_month == d._month)
			return _day < d._day;
		else
			return false;
	}
	else
		return false;
}
bool Date::operator<=(const Date& d) const {
	return *this < d || *this == d;
}
/*
bool Date::operator>(const Date& d) const {
	if (_year  > d._year)
		return true;
	else if (_year == d._year) {
		if (_month  > d._month)
			return true;
		else if (_month == d._month)
			return _day  > d._day;
		else
			return false;
	}
	else
		return false;
}
*/
bool Date::operator>(const Date& d) const {
	return !(*this < d);
}
bool Date::operator>=(const Date& d) const {
	return *this > d || *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
{
	return !(*this == d);
}

2.3日期的加减运算

对于加减运算看起来简单,思路也挺丰富,但是通过代码实现还是有一定的难度的,接下来小编带领大家分析实现。

日期加天数

对于日期加天数,我们要进位并且还要判断是否满年和满该月,然后向前加

Date& Date::operator+=(int day) {
	if (day < 0) {
		return *this -= (-day);
	}
    _day += day; // 将传入的天数加到当前日期的天数上
	while (_day > (Getday(_year, _month))) { // 检查当前日期是否超过了这个月的天数
		_day -= Getday(_year, _month); // 减去这个月的天数
		_month++; // 月份加一
		if (_month == 13) { // 如果月份超过 12
			_month = 1; // 重置为 1
			_year++; // 年份加一
		}
	}
	return *this; // 返回当前对象的引用
}

这里的返回是传引用返回,在原来的日期上进行修改,更好的支持链式操作,当然我们也可以通过传值返回,只不过要拷贝一份,加临时变量。

为了方便区分理解,传引用我们定义为+=,传值为+

传值调用法可以借鉴+=的思路,创建一个临时变量temp,在所有成员前面加temp.即可

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

	}
	return temp;
}

当然一个简便的方法是临时生成一个temp,通过+=函数改变temp,再返回temp。

Date Date::operator+(int day)const {
	Date temp = *this;
	temp += day;
	return temp;
}

日期减天数

我们要采取借位的方法,通过用上一个月份的天数加到_day上,直到>0为止。

Date& Date::operator-=(int day) {
    if (day < 0) {
	return *this += (-day);
}
    _day -= day; // 从当前日期的天数中减去传入的天数
    while (_day <= 0) { // 检查当前日期的天数是否小于或等于0
        _month--; // 减少月份
        if (_month == 0) { // 如果月份变为0,意味着需要回到上一年
            _month = 12; // 将月份重置为12
            _year--; // 年份减少
        }
        _day += Getday(_year, _month); // 为当前月份添加天数
    }
    return *this; // 返回当前对象的引用,
}

同样分为传值和传引用,实现类似于加天数,所以不多加赘述了。

Date Date::operator-(int day) const {
	Date temp = *this;
	temp -= day;
	return temp;
}

 这里额外还有日期的加加减减,其中又分为后置和前置C++祖师爷有自己的规定方法,记住就行,而实现方法就直接调用加减天数即可,因为加减天数只是限制成了一天而已。

在头文件的注释中也提到了规定:


    // ++d1 -> d1.operator++()
    Date& operator++();
    
    // d1++ -> d1.operator++(0)
    // 为了区分,构成重载,给后置++,强行增加了一个int形参
    // 这里不需要写形参名,因为接收值是多少不重要,也不需要用
    // 这个参数仅仅是为了跟前置++构成重载区分

    Date operator++(int);
    Date& operator--();
    Date operator--(int);

Date& Date:: operator++() {
	*this += 1;
	return* this;
}
Date Date::operator++(int) {
	Date temp = *this;
	temp += 1;
	return temp;
}

Date& Date:: operator--() {
	*this -= 1;
	return*this;
}
Date Date::operator--(int) {
	Date temp = *this;
	temp -= 1;
	return temp;
}

 这里同样分为了传值和传引用。

日期-日期

因为日期+日期没有实际意义,所以我们就不用实现了。对于日期-日期,首先我们想到了年月日分别相减再换成天,或者说都换算成天数相减,两者都要计算闰年个数,较为麻烦。

这里我们可以借鉴加加的思想,让小的日期一直+,直到等于大的日期,加的次数就是日期差了。

int Date::operator-(const Date& d) const {
	int flag = 1;
	Date max = *this;
	Date min = d;
	if (*this <  d) {
		max = d;
		min = *this;
		flag = -1;
	}
	int n = 0;
	while (min!=max) {
		++min;
		n++;

	}
	return n * flag;
}

这里用了flag=1/-1,是为了满足日期相减的正负,因为大小日期是个函数内的局部,且小到大算出来的n都是正的,n*flag就满足了实际情况。

3.日期类的输入输出

我们如果想自己定义一个输出输入的话,这里就要用到流输入输出,因为是自定义类型输入输出,所以我们要自己创建。

void Date::operator<<(ostream& out)
{
	out << _year << "年" << _month << "月" << _day << "日" << endl;
}

这种定义实现的时候有个缺陷,因为有默认的this指针,所对应参数就有问题,所以用正常的顺序cout<<就是错的。

Date d(2024, 8, 2);
d << cout;//正确格式

因为Date* this占据了⼀个参数位置,使⽤d<<cout不符合习惯。 

所以我们设置了两个参数,ostream& out, const Date&d,理所当然的我们放在了类外,因而又出现了一个问题,访问不了私有成员,在前面的博客中我们提到了几种解决方法,在这里我们采取友元函数的方法,在函数前面加friend,在类里定义,具体的友元讲解在类的对象完结撒花会提到。

函数的模板定义在头文件有详细注明。

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 << "输入日期非法:";
			d.Print();
			cout << "请重新输入!!!" << endl;
		}
		else
		{
			break;
		}
	}

	return in;
}

输入与输出是类似的思路,只是这里设置了循环来控制输入。

4.测试代码参考

#include "Date.h"
void test1() {
	Date d1(2024, 7, 30);
	d1.Print();
	++d1;
	++d1;
	Date ret = d1++;
	ret.Print();
	d1.Print();
	// Date d2 = d1 + 100;
	 //d2.Print();
	 //d1 += 100;
	 //d1.Print();
}
void test2() {
	Date d1(2024, 7, 30);
	Date d2(2024, 7, 30);
	bool result = (d1 >= d2);
	d1 -= 100;
	d1.Print();
	Date d3 = d1 - 100;
	d3.Print();
	--d3;
	d3.Print();
}
void test3() {
	Date d(2024, 8, 2);
	//d << cout;
	cout << d;
	Date d1, d2;
	cin >> d1 >> d2;
	cout << d1 << d2 << endl;
}
int main() {
	
	//Date d2 = d1 + 100;
	//d2.Print();
	//test2();
	/*
	Date d1(2024, 7, 31);
	Date d2(2024, 12, 29);
	cout << d1 - d2 << endl;
	cout << d2 - d1 << endl;
	*/
	//test1();
}

可以根据自己的需求进行修改。

结束语

本篇博客到此结束,大家可以根据自己的需求自己添加功能,小编实力有限,所以功能只能实现这么多,大家可以在评论区多多发表自己的观点。

最后给小编点赞支持下吧!!!

评论 73
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值