今天简单来说一下日期类的实现,算是一个小的练习吧。
1.日期类是什么?
日期是表示时间的一种计量数字。比如我们常说公历农历。
在这里我分享的这个日期类是大大进行简化的,比如时间精度只精确到天,有效范围是0年0月0日之后…事实上,日期是非常复杂的,比如精度要精确到秒,还有大量特殊的日期,是大概1600年之后每年才比较正常,之前由于历史原因,有各种人为添加日子的情况…再加上润年的考虑…总之就是很麻烦。
2.日期类框架的构建
我们是用CPP语言来写一个简单的日期类的。那我们就按照CPP的思想先描述后组织进行依次实现。
我们用一个类来描述对应的日期,把日期看作一个对象来进行实现。
// Date.h
class Date
{
private:
int _day;
int _month;
int _year;
}
好的,我们就简单的写了一个类来描述日期这一对象,然后我们再进行组织完善。
3.构造函数重写
虽说自定义类型编译器会默认给我们生成一个默认构造函数,但是显然不能满足我们的需求。
在重写构造函数的时候,我们需要考虑构造函数构造出来的日期对象是否合法的一个问题。
// Date.h
class Date
{
private:
int _day;
int _month;
int _year;
public:
Date(int year = 1, int month = 1, int day = 1);
}
这里实现一个全缺省的构造函数,不用写多个重载构造了,这样写比较方便。
然后我们在Date.cpp的文件里实现我们的重写的构造函数:先不要被劝退,从下往上看很简单,只是稍微长一点而已。下面是Date.cpp内容
// Date.cpp
bool CheckLeapYear(int year, int month)
{
if ((!(year % 4) && (year % 100)) || !(year % 400)) return true;
else return false;
}
int GetMonthDays(int year, int month)
{
assert(month >= 1 && month <= 12);
static int Months[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
if (month == 2 && CheckLeapYear(year, month)) return Months[month] + 1;
else return Months[month];
}
bool Date::CheckInvalid()
{
if (_year < 0 || _month > 12 || _month < 1 || _day < 0 || _day > GetMonthDays(_year, _month)) return false;
else return true;
}
Date::Date(int year, int month, int day)
:_year(year),_month(month),_day(day)
{
if (!CheckInvalid()) std::cout << "error reprint" << std::endl;
}
然后我又考虑到,我们每次创建一个日期对象,都需要调用构造函数,那么每次都要调用CheckInvalid()和GetMonthDays()函数,也就是说这俩函数比较短小且被频繁调用,我干脆直接把他实现成内联函数得了。
然后就可以实现为下面这个样子:
// Date.cpp
#include"Date.h"
bool Date::CheckInvalid()
{
if (_year < 0 || _month > 12 || _month < 1 || _day < 0 || _day > GetMonthDays(_year, _month)) return false;
else return true;
}
Date::Date(int year, int month, int day)
:_year(year),_month(month),_day(day)
{
if (!CheckInvalid()) std::cout << "error reprint" << std::endl;
}
// Date.h
class Date
{
private:
int _day;
int _month;
int _year;
public:
Date(int year = 1, int month = 1, int day = 1); //简单的缺省构造函数 Date() --> CheckInvalid() --> CheckLeapYear --> GetMonthDays
bool CheckInvalid();
inline bool CheckLeapYear(int year, int month)
{
if ((!(year % 4) && (year % 100)) || !(year % 400)) return true;
else return false;
}
inline int GetMonthDays(int year, int month)
{
assert(month >= 1 && month <= 12);
static int Months[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
if (month == 2 && CheckLeapYear(year, month)) return Months[month] + 1;
else return Months[month];
}
}
做好了上面这些,日期类的构造函数算是重写完了。
我们再写一个打印日期类的函数,方便我们以后测试结果。
4.打印函数
不知道大家写代码什么习惯,因为我现在是初学者,所以写一些代码都是比较基础的,所以很多时候直接开始写一个打印函数比较容易观察现象,定位错误。
void Date::DatePrint() const
{
std::cout << this->_year << "/" << this->_month << "/" << this->_day << std::endl;
}
注意哈,写完了打印函数要记得把这个函数声明到Date.h中去。
5.常见的Date判断函数
我们根据生活常识可知,日期类是可以进行比较大小的,比如说2024.7.25就是比2024.7.24大呀,又或者说2024.7.25与2024.7.24不是同一天(显然这是废话)…所以我们写的Date也要支持这些比较操作。
大概有下面声明的这几个比较判断操作:
// Date.h
class Date
{
private:
int _day;
int _month;
int _year;
public:
Date(int year = 1, int month = 1, int day = 1); //简单的缺省构造函数 Date() --> CheckInvalid() --> CheckLeapYear --> GetMonthDays
bool CheckInvalid();
inline bool CheckLeapYear(int year, int month)
{
if ((!(year % 4) && (year % 100)) || !(year % 400)) return true;
else return false;
}
inline int GetMonthDays(int year, int month)
{
assert(month >= 1 && month <= 12);
static int Months[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
if (month == 2 && CheckLeapYear(year, month)) return Months[month] + 1;
else return Months[month];
}
void DatePrint() const; // 打印Date类对象信息
// 一些常见的判断操作
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;
然后我们着手实现的时候,因为这里有六个判断操作,并且他们之间有特定的关系,比如说小于等价于大于等于取反,不等于等价于等于取反,这个地方就可以实现两个判断操作,剩下的一顿复用就行。
#include"Date.h"
bool Date::CheckInvalid()
{
if (_year < 0 || _month > 12 || _month < 1 || _day < 0 || _day > GetMonthDays(_year, _month)) return false;
else return true;
}
Date::Date(int year, int month, int day)
:_year(year),_month(month),_day(day)
{
if (!CheckInvalid()) std::cout << "error reprint" << std::endl;
}
void Date::DatePrint() const
{
std::cout << this->_year << "/" << this->_month << "/" << this->_day << std::endl;
}
bool Date::operator<(const Date& d) const
{
if (this->_year < d._year) return true;
else if(this->_year == d._year)
{
if (this->_month < d._month) return true;
else if (this->_month == d._month && this->_day < d._day) return true;
}
return false;
}
bool Date::operator==(const Date& d) const
{
if (_year == d._year && _month == d._month && _day == d._day) return true;
else return false;
}
bool Date::operator<=(const Date& d) const
{
if (*this < d || *this == d) return true;
else return false;
}
bool Date::operator>(const Date& d) const
{
if (!(*this <= d)) return true;
else return false;
}
bool Date::operator>=(const Date& d) const
{
if (!(*this < d)) return true;
else return false;
}
bool Date::operator!=(const Date& d) const
{
if (!(*this == d)) return true;
else return false;
}
上面这六个判断函数就是典型的代码复用例子,以后写代码可以多注意这种代码复用技巧,比较省力是吧。
我们写完了日期之间的比较判断,其实日期也是可以进行运算的。
6.日期类的运算
不知道大家注意到没有,日期是可以进行运算的。比如今天是2024年7月25日,那么在今天的基础上减一操作,也就是前一天是2024.7.14,当然也可以加上30天,看看三十天后是几月几号。我们也可以拿到两个日期来算他们之间差多少天…当然需要注意的是有些日期运算是没有意义的,比如一个日期+另一个日期是没有意义的,我们也就不必实现没有意义的运算。
#pragma once
#include<iostream>
#include<assert.h>
class Date
{
private:
int _day;
int _month;
int _year;
public:
Date(int year = 1, int month = 1, int day = 1); //简单的缺省构造函数 Date() --> CheckInvalid() --> CheckLeapYear --> GetMonthDays
bool CheckInvalid();
inline bool CheckLeapYear(int year, int month)
{
if ((!(year % 4) && (year % 100)) || !(year % 400)) return true;
else return false;
}
inline int GetMonthDays(int year, int month)
{
assert(month >= 1 && month <= 12);
static int Months[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
if (month == 2 && CheckLeapYear(year, month)) return Months[month] + 1;
else return Months[month];
}
void DatePrint() const; // 打印Date类对象信息
// 一些常见的判断操作
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;
// +/+=/++/-/-=
Date& operator+=(const int day);
Date operator+(const int day) const;
Date& operator++(); // 前置++
Date operator++(int); // 后置++
int operator-(Date& d);
实现一下:
Date& Date::operator+=(const int day)
{
this->_day += day;
while (_day > GetMonthDays(this->_year, this->_month))
{
this->_day -= GetMonthDays(this->_year, this->_month);
this->_month++;
if (this->_month > 12)
{
this->_month = 1;
this->_year++;
}
}
return *this;
}
Date Date::operator+(const int day) const
{
Date temp = *this; // 拷贝构造
temp += day;
return temp;
}
Date& Date::operator++() // 前置++
{
*this += 1;
return *this;
}
Date Date::operator++(int) // 后置++
{
Date temp = *this;
*this += 1;
return temp;
}
int Date::operator-(Date& d)
{
int sub = 0;
Date max, min;
if (*this > d)
{
max = *this;
min = d;
}
else
{
max = d;
min = *this;
}
while (max != min)
{
min++;
sub++;
}
return sub;
}
处理完了日期类的运算,基本最难的也就过去了,还有个比较重要的流插入和流提取去重写,基本日期类就写完了。
7.流插入、流提取
#pragma once
#include<iostream>
#include<assert.h>
class Date
{
private:
int _day;
int _month;
int _year;
public:
Date(int year = 1, int month = 1, int day = 1); //简单的缺省构造函数 Date() --> CheckInvalid() --> CheckLeapYear --> GetMonthDays
bool CheckInvalid();
inline bool CheckLeapYear(int year, int month)
{
if ((!(year % 4) && (year % 100)) || !(year % 400)) return true;
else return false;
}
inline int GetMonthDays(int year, int month)
{
assert(month >= 1 && month <= 12);
static int Months[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
if (month == 2 && CheckLeapYear(year, month)) return Months[month] + 1;
else return Months[month];
}
void DatePrint() const; // 打印Date类对象信息
// 一些常见的判断操作
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;
// +/+=/++/-/-=
Date& operator+=(const int day);
Date operator+(const int day) const;
Date& operator++(); // 前置++
Date operator++(int); // 后置++
int operator-(Date& d);
// 流插入和流提取
friend std::ostream& operator<<(std::ostream& out, const Date& d);
friend std::istream& operator>>(std::istream& in, Date& d);
};
std::ostream& operator<<(std::ostream& out, const Date& d);
std::istream& operator>>(std::istream& in, Date& d);
std::ostream& operator<<(std::ostream& out, const Date& d)
{
out << d._year << "年" << d._month << "月" << d._day << "日" << std::endl;
return out;
}
std::istream& operator>>(std::istream& in, Date& d)
{
while (true)
{
in >> d._year >> d._month >> d._day;
if (!d.CheckInvalid())
{
std::cout << "error, refail" << std::endl;
continue;
}
else
{
break;
}
}
return in;
}
这个流插入和流提取用到了友元函数、并且与其他的函数不同,其他函数是声明在了Date类内,而流插入提取在类外。
这个主要是因为流插入提取如果声明在类内,第一个参数就一定是隐藏this指针,到时候我们在外面调用流插入的时候写法会很离谱。这里就不多说了。详见下图:
好了,终于说完了实现Date类的大体思路了,感觉我只是说了一下大体的实现思路,每个函数具体怎么去实现,我想代码是最具体的,当然有些地方可能有疑问,问什么这么做为什么这样处理,先思考,不明白可以评论区里问。
下面把全部代码放下面,有需要可以看看全部代码。
8.全部代码
// Date.h
#pragma once
#include<iostream>
#include<assert.h>
class Date
{
private:
int _day;
int _month;
int _year;
public:
Date(int year = 1, int month = 1, int day = 1); //简单的缺省构造函数 Date() --> CheckInvalid() --> CheckLeapYear --> GetMonthDays
bool CheckInvalid();
inline bool CheckLeapYear(int year, int month)
{
if ((!(year % 4) && (year % 100)) || !(year % 400)) return true;
else return false;
}
inline int GetMonthDays(int year, int month)
{
assert(month >= 1 && month <= 12);
static int Months[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
if (month == 2 && CheckLeapYear(year, month)) return Months[month] + 1;
else return Months[month];
}
void DatePrint() const; // 打印Date类对象信息
// 一些常见的判断操作
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;
// +/+=/++/-/-=
Date& operator+=(const int day);
Date operator+(const int day) const;
Date& operator++(); // 前置++
Date operator++(int); // 后置++
int operator-(Date& d);
// 流插入和流提取
friend std::ostream& operator<<(std::ostream& out, const Date& d);
friend std::istream& operator>>(std::istream& in, Date& d);
};
std::ostream& operator<<(std::ostream& out, const Date& d);
std::istream& operator>>(std::istream& in, Date& d);
// Date.cpp
#include"Date.h"
bool Date::CheckInvalid()
{
if (_year < 0 || _month > 12 || _month < 1 || _day < 0 || _day > GetMonthDays(_year, _month)) return false;
else return true;
}
Date::Date(int year, int month, int day)
:_year(year),_month(month),_day(day)
{
if (!CheckInvalid()) std::cout << "error reprint" << std::endl;
}
void Date::DatePrint() const
{
std::cout << this->_year << "/" << this->_month << "/" << this->_day << std::endl;
}
bool Date::operator<(const Date& d) const
{
if (this->_year < d._year) return true;
else if(this->_year == d._year)
{
if (this->_month < d._month) return true;
else if (this->_month == d._month && this->_day < d._day) return true;
}
return false;
}
bool Date::operator==(const Date& d) const
{
if (_year == d._year && _month == d._month && _day == d._day) return true;
else return false;
}
bool Date::operator<=(const Date& d) const
{
if (*this < d || *this == d) return true;
else return false;
}
bool Date::operator>(const Date& d) const
{
if (!(*this <= d)) return true;
else return false;
}
bool Date::operator>=(const Date& d) const
{
if (!(*this < d)) return true;
else return false;
}
bool Date::operator!=(const Date& d) const
{
if (!(*this == d)) return true;
else return false;
}
Date& Date::operator+=(const int day)
{
this->_day += day;
while (_day > GetMonthDays(this->_year, this->_month))
{
this->_day -= GetMonthDays(this->_year, this->_month);
this->_month++;
if (this->_month > 12)
{
this->_month = 1;
this->_year++;
}
}
return *this;
}
Date Date::operator+(const int day) const
{
Date temp = *this; // 拷贝构造
temp += day;
return temp;
}
Date& Date::operator++() // 前置++
{
*this += 1;
return *this;
}
Date Date::operator++(int) // 后置++
{
Date temp = *this;
*this += 1;
return temp;
}
int Date::operator-(Date& d)
{
int sub = 0;
Date max, min;
if (*this > d)
{
max = *this;
min = d;
}
else
{
max = d;
min = *this;
}
while (max != min)
{
min++;
sub++;
}
return sub;
}
std::ostream& operator<<(std::ostream& out, const Date& d)
{
out << d._year << "年" << d._month << "月" << d._day << "日" << std::endl;
return out;
}
std::istream& operator>>(std::istream& in, Date& d)
{
while (true)
{
in >> d._year >> d._month >> d._day;
if (!d.CheckInvalid())
{
std::cout << "error, refail" << std::endl;
continue;
}
else
{
break;
}
}
return in;
}
EOF