今天简单来说一下日期类的实现,算是一个小的练习吧。
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