当前位置:   article > 正文

【C++】类与对象的项目实践 — 日期管理工具

【C++】类与对象的项目实践 — 日期管理工具

在这里插入图片描述

根据我们对C++特性的学习,我们现在可以尝试下一些新玩法,来强化我们对C++的理解。

项目背景

在现代的软件开发中,日期作为一个常见的基础需求,广泛用于各类系统的日程管理,数据分析,交易记录等场景。但是C++库中的时间日期功能比较有限,无法满足复杂的开发需求。为此我们需要开发一款简单高效的“日期类”C++项目。
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/9f15918ab9814067a9aba1dd289090b4.png

项目需求

  1. 日期结构设计:我们需要实现一个名为“Date”的C++自定义类型,包含年(_year),月(_month),日(_day)。并相应提供构造函数,析构函数,拷贝复制函数等函数。
  2. 日期运算方法:实现日期的加减运算,支持用户通过增加或减少年、月、日来实现新的日期对象。同时,提供比较两个日期大小的方法,包括< 、>、 ==、 <= 、>= 、!=等关系操作符的重载。
  3. 日期有效性检查:Date类需要实现对日期有效性的严格检查,确保月份正常,保证闰年的判断,符合各个月份的实际天数。
  4. 日期格式转换:提供将Date对象转换为“XXXX—YY—ZZ”的方法,同时也支持从标准“XXXX—YY—ZZ”字符串中解析创建Date对象。
  5. 实用工具方法:提供获取当前日期,判断是否为闰年,计算两个日期的天数差等功能。

以上就是该项目的基本需求,请务必确保程序的健壮性与可维护性。

项目实现

1 日期结构设计

该部分我们给出以下框架:

class Date{

public:
	// 获取某年某月的天数
	int GetMonthDay(int year, int month);
	//展示日期
	void show();
	// 全缺省的构造函数
	Date(int year = 1900, int month = 1, int day = 1);
	// 拷贝构造函数
    // d2(d1)
	Date(const Date& d);
	// 析构函数
	~Date();
	
	//---------------------------------------------------------------
	// 赋值运算符重载

    // d2 = d3 -> d2.operator=(&d2, d3)
	Date& 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);
	// 后置--
	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);
	//-----------------------------------------------
	// 转换为字符串“ YYYY-MM-DD ”
	string toString() const;
	//通过字符串构建对象
	static Date fromString(const string& dateStr);

private:
	int _year;
	int _month;
	int _day;

};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63

这里给出较为全面的框架:

  1. 成员变量:私有成员变量 _year、_month 和 _day 分别表示年份、月份和日期。

  2. 构造函数:
    全缺省构造函数,默认日期为1900年1月1日。
    拷贝构造函数,复制给定日期对象的所有信息。

  3. 赋值运算符重载 (operator=):用于拷贝另一个Date对象的日期信息到当前对象。

  4. 算术运算符重载:
    += 和 -= 运算符用于日期增加或减少指定天数。
    +和 - 运算符分别用于返回增加或减少指定天数后的日期对象,以及两个日期之间的天数差。

  5. 自增/自减运算符重载:
    前缀和后缀形式的 ++ 与 – 运算符,用于向前或向后移动一天。

  6. 关系运算符重载:
    <、>、>=、<= 和 == 分别用于比较两个日期的大小关系。
    != 判断两个日期是否不相等。

  7. 方法:
    show() 用于输出日期。
    GetMonthDay() 根据年份和月份获取该月的天数,考虑了闰年的特殊情况。

  8. 析构函数:
    简单地将日期成员变量设为0,但在实际应用中这通常不是必要的,因为类的生命周期结束后,系统会自动释放其占用的内存资源。

下面我们逐一实现功能:

2 构造函数

2.1 全缺省构造函数

首先我们需要提供一个全缺省的构造函数,方便对象的实例化。
这里我们提供默认的(1900 -1- 1)作为缺省值

Date::Date(int year , int month , int day ) {
	_year = year;
	_month = month;
	_day = day;
}
  • 1
  • 2
  • 3
  • 4
  • 5

2.2 拷贝构造函数

然后我们还需要一个拷贝构造函数,让我们更加灵活的创建对象

Date::Date(const Date& d) {
	_year = d._year;
	_month = d._month;
	_day = d._day;
}
  • 1
  • 2
  • 3
  • 4
  • 5

2.3 析构函数

析构函数简单一写即可,因为我们没有开辟空间,不需要考虑复杂问题。

3 赋值运算符重载

这里我们需要实现:
= + - += -= 前置++ 后置++ 前置-- 后置–
加和减原理类似,讲解中只以加为例,详细代码请看结尾全部代码。

3.1 =重载

注意返回值类型,这里传回引用,效率更好。

Date& Date::operator=(const Date& d) {
	_year = d._year;
	_month = d._month;
	_day = d._day;
    //灵活使用this指针
	return *this;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

3.2 += 重载

这里我们先实现+= 之后再实现+,这样可以避免多次创建变量,效率更高。
首先对于+= 我们需要准确知道该月的月份,保证日期的有效性。

int Date::GetMonthDay(int year, int month) {
	int day[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 day[month];
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

完成+=重载

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

	_day += day;
	while (_day > GetMonthDay(_year, _month)) {
		_day -= GetMonthDay(_year, _month);
		_month++;
		if (_month > 12) {
			_year++;
			_month = 1;
		}

	}
	return *this;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

+重载

实现了+=之后我们就好实现+了
注意这里不能传回引用 , 这里是临时变量,传引用会导致错误。

Date Date::operator+(int day) {
	Date temp(*this);
	temp += day;
	return temp;
}
  • 1
  • 2
  • 3
  • 4
  • 5

前置++ 和 后置++

前置后置这里使用了不同参数来做区分。不得感叹祖师爷的巧妙。

//前置++
Date& Date::operator++() {
//直接返回+=后答案即可
	return *this += 1;
}
//后置++ 不能传引用
Date Date::operator++(int) {
//创建一个临时变量来储存 ++前的值
	Date temp(*this);
	*this += 1;
	//返回++前的值
	return temp;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

4 关系操作符重载

这里十分巧妙,我们只需要实现== > 即可通过巧妙使用完成全部操作符重载:

bool Date::operator==(const Date& d) {
	return d._year == _year && d._month == _month && d._day == _day;
}

bool Date::operator>(const Date& d) {
	if (_year < d._year) return false;
	else if (_month < d._month) return false;
	else if (_day < d._day) return false;
	else return true;
		
}

bool Date::operator >= (const Date& d) {
	return *this > d || *this == d;
}

bool Date::operator < (const Date& d) {
	return !(*this >= d);
}

bool Date::operator <= (const Date& d) {
	return !(*this > d);
}

bool Date::operator != (const Date& d) {
	return !(*this == d);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

5 工具方法

5.1 计算日期差

这里使用朴素算法,逐渐++到较大日期即可即可

//注意正负
int Date::operator-(const Date& d) {
	//默认前值大
	int flag = 1;
	Date max = *this;
	Date min = d;

	if (max < min) {
		flag = -1;
		max = d;
		min = *this;
	}
	int day = 0;
	while (min != max){
		min++;
		day++;
	}
	return flag * day;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

5.2 日期转换为字符串

使用库函数轻松实现:

string Date::toString() const {
	char buffer[11]; // 预留足够的空间存储 "YYYY-MM-DD"
	snprintf(buffer, sizeof(buffer), "%04d-%02d-%02d", _year, _month, _day);
	return string(buffer);
}
  • 1
  • 2
  • 3
  • 4
  • 5

5.3 通过字符串构建对象

这一步比较复杂:
首先在声明中加入static 保证可以随时调用。
然后需要保证日期的合法性
最后返回类对象

static Date fromString(const string& dateStr);

 Date Date::fromString(const std::string& dateStr) {
	int year = 0, month = 0, day= 0;
	sscanf(dateStr.c_str(), "%d-%d-%d", &year, &month, &day); // 使用sscanf解析字符串
	Date d(year,month,day);
	// 在这里应该添加必要的日期合法性检查
	if (month < 1 || month > 12) {
		throw std::invalid_argument("月份错误\n");
	}
	int maxDays = d.GetMonthDay(year, month);
	if (day < 1 || day > maxDays) {
		throw std::invalid_argument("给定月份和年份的日期值无效\n");
	}

	return Date(year, month, day);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

完整源代码

Date.h

#pragma once

#include<iostream>
using namespace std;
class Date{

public:
	// 获取某年某月的天数
	int GetMonthDay(int year, int month);
	//展示日期
	void show();
	// 全缺省的构造函数
	Date(int year = 1900, int month = 1, int day = 1);
	// 拷贝构造函数
    // d2(d1)
	Date(const Date& d);
	// 析构函数
	~Date();
	// 赋值运算符重载

    // d2 = d3 -> d2.operator=(&d2, d3)
	Date& 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);
	// 后置--
	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);
	// 转换为字符串“ YYYY-MM-DD ”
	string toString() const;

	static Date fromString(const string& dateStr);

private:
	int _year;
	int _month;
	int _day;

};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65

Date.cpp

#include"Date.h"

void Date::show() {
	
	printf("%4d 年 %2d 月 %2d 日\n",_year,_month,_day);
}

int Date::GetMonthDay(int year, int month) {
	int day[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 day[month];
}

Date::Date(int year , int month , int day ) {
	_year = year;
	_month = month;
	_day = day;
}

Date::Date(const Date& d) {
	_year = d._year;
	_month = d._month;
	_day = d._day;
}

Date::~Date() {
	_year = 0;
	_month = 0;
	_day = 0;
}

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

	_day += day;
	while (_day > GetMonthDay(_year, _month)) {
		_day -= GetMonthDay(_year, _month);
		_month++;
		if (_month > 12) {
			_year++;
			_month = 1;
		}

	}
	return *this;
}

Date& Date::operator=(const Date& d) {
	_year = d._year;
	_month = d._month;
	_day = d._day;

	return *this;
}

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

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

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

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

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

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

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

bool Date::operator==(const Date& d) {
	return d._year == _year && d._month == _month && d._day == _day;
}

bool Date::operator>(const Date& d) {
	if (_year < d._year) return false;
	else if (_month < d._month) return false;
	else if (_day < d._day) return false;
	else return true;
		
}

bool Date::operator >= (const Date& d) {
	return *this > d || *this == d;
}

bool Date::operator < (const Date& d) {
	return !(*this >= d);
}

bool Date::operator <= (const Date& d) {
	return !(*this > d);
}

bool Date::operator != (const Date& d) {
	return !(*this == d);
}

int Date::operator-(const Date& d) {

	Date max = *this;
	Date min = d;

	int flag = 1;

	if (max < min) {
		flag = -1;
		max = d;
		min = *this;
	}

	int day = 0;

	while (min != max)
	{
		min++;
		day++;
	}
	return flag * day;
}

string Date::toString() const {
	char buffer[11]; // 预留足够的空间存储 "YYYY-MM-DD"
	snprintf(buffer, sizeof(buffer), "%04d-%02d-%02d", _year, _month, _day);
	return string(buffer);
}

 Date Date::fromString(const std::string& dateStr) {
	int year = 0, month = 0, day= 0;
	sscanf(dateStr.c_str(), "%d-%d-%d", &year, &month, &day); // 使用sscanf解析字符串
	Date d(year,month,day);
	// 在这里应该添加必要的日期合法性检查
	if (month < 1 || month > 12) {
		throw std::invalid_argument("月份错误\n");
	}
	int maxDays = d.GetMonthDay(year, month);
	if (day < 1 || day > maxDays) {
		throw std::invalid_argument("给定月份和年份的日期值无效\n");
	}

	return Date(year, month, day);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176

Thanks♪(・ω・)ノ谢谢阅读!!!

下一篇文章见!!!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/147826
推荐阅读
相关标签
  

闽ICP备14008679号