项目背景
在现代的软件开发中,日期作为一个常见的基础需求,广泛用于各类系统的日程管理,数据分析,交易记录等场景。但是C 库中的时间日期功能比较有限,无法满足复杂的开发需求。为此我们需要开发一款简单高效的“日期类”C 项目。
项目需求
- 日期结构设计:我们需要实现一个名为“Date”的C 自定义类型,包含年(_year),月(_month),日(_day)。并相应提供构造函数,析构函数,拷贝复制函数等函数。
- 日期运算方法:实现日期的加减运算,支持用户通过增加或减少年、月、日来实现新的日期对象。同时,提供比较两个日期大小的方法,包括< 、>、 ==、 <= 、>= 、!=等关系操作符的重载。
- 日期有效性检查:Date类需要实现对日期有效性的严格检查,确保月份正常,保证闰年的判断,符合各个月份的实际天数。
- 日期格式转换:提供将Date对象转换为“XXXX—YY—ZZ”的方法,同时也支持从标准“XXXX—YY—ZZ”字符串中解析创建Date对象。
- 实用工具方法:提供获取当前日期,判断是否为闰年,计算两个日期的天数差等功能。
以上就是该项目的基本需求,请务必确保程序的健壮性与可维护性。
项目实现
1 日期结构设计
该部分我们给出以下框架:
代码语言:javascript复制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;
};
这里给出较为全面的框架:
- 成员变量:私有成员变量 _year、_month 和 _day 分别表示年份、月份和日期。
- 构造函数: 全缺省构造函数,默认日期为1900年1月1日。 拷贝构造函数,复制给定日期对象的所有信息。
- 赋值运算符重载 (operator=):用于拷贝另一个Date对象的日期信息到当前对象。
- 算术运算符重载: = 和 -= 运算符用于日期增加或减少指定天数。 和 - 运算符分别用于返回增加或减少指定天数后的日期对象,以及两个日期之间的天数差。
- 自增/自减运算符重载: 前缀和后缀形式的 与 – 运算符,用于向前或向后移动一天。
- 关系运算符重载: <、>、>=、<= 和 == 分别用于比较两个日期的大小关系。 != 判断两个日期是否不相等。
- 方法: show() 用于输出日期。 GetMonthDay() 根据年份和月份获取该月的天数,考虑了闰年的特殊情况。
- 析构函数: 简单地将日期成员变量设为0,但在实际应用中这通常不是必要的,因为类的生命周期结束后,系统会自动释放其占用的内存资源。
下面我们逐一实现功能:
2 构造函数
2.1 全缺省构造函数
首先我们需要提供一个全缺省的构造函数,方便对象的实例化。 这里我们提供默认的(1900 -1- 1)作为缺省值
代码语言:javascript复制Date::Date(int year , int month , int day ) {
_year = year;
_month = month;
_day = day;
}
2.2 拷贝构造函数
然后我们还需要一个拷贝构造函数,让我们更加灵活的创建对象
代码语言:javascript复制Date::Date(const Date& d) {
_year = d._year;
_month = d._month;
_day = d._day;
}
2.3 析构函数
析构函数简单一写即可,因为我们没有开辟空间,不需要考虑复杂问题。
3 赋值运算符重载
这里我们需要实现:
= - = -= 前置 后置 前置-- 后置–
加和减原理类似,讲解中只以加为例,详细代码请看结尾全部代码。
3.1 =重载
注意返回值类型,这里传回引用,效率更好。
代码语言:javascript复制Date& Date::operator=(const Date& d) {
_year = d._year;
_month = d._month;
_day = d._day;
//灵活使用this指针
return *this;
}
3.2 = 重载
这里我们先实现 = 之后再实现 ,这样可以避免多次创建变量,效率更高。 首先对于 = 我们需要准确知道该月的月份,保证日期的有效性。
代码语言:javascript复制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];
}
完成 =重载
代码语言:javascript复制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;
}
重载
实现了 =之后我们就好实现 了 注意这里不能传回引用 , 这里是临时变量,传引用会导致错误。
代码语言:javascript复制Date Date::operator (int day) {
Date temp(*this);
temp = day;
return temp;
}
前置 和 后置
前置后置这里使用了不同参数来做区分。不得感叹祖师爷的巧妙。
代码语言:javascript复制//前置
Date& Date::operator () {
//直接返回 =后答案即可
return *this = 1;
}
//后置 不能传引用
Date Date::operator (int) {
//创建一个临时变量来储存 前的值
Date temp(*this);
*this = 1;
//返回 前的值
return temp;
}
4 关系操作符重载
这里十分巧妙,我们只需要实现== > 即可通过巧妙使用完成全部操作符重载:
代码语言:javascript复制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);
}
5 工具方法
5.1 计算日期差
这里使用朴素算法,逐渐 到较大日期即可即可
代码语言:javascript复制//注意正负
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;
}
5.2 日期转换为字符串
使用库函数轻松实现:
代码语言:javascript复制string Date::toString() const {
char buffer[11]; // 预留足够的空间存储 "YYYY-MM-DD"
snprintf(buffer, sizeof(buffer), "d-d-d", _year, _month, _day);
return string(buffer);
}
5.3 通过字符串构建对象
这一步比较复杂: 首先在声明中加入static 保证可以随时调用。 然后需要保证日期的合法性 最后返回类对象
代码语言:javascript复制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);
}
完整源代码
Date.h
代码语言:javascript复制#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;
};
Date.cpp
代码语言:javascript复制#include"Date.h"
void Date::show() {
printf("M 年 - 月 - 日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), "d-d-d", _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);
}