08.5【C++ 初阶】实现一个相对完整的日期类--附带源码
文章目录
- 前言
- 分析
- Date.h
- Date.cpp
- 一些测试代码:(main.cpp)
- 全文总结
前言
本文是对之前学习知识的小小总结实践。
分析
功能分析:
- 日期类简单的记录我们的日期时间,包括年、月、日
- 支持日期类对象之间的拷贝赋值
- 两日期类对象相减得到天数差
- 日期类对象减特定天数得到新日期
- 日期类对象支持cout打印
- 日期类对象支持cin输入赋值
- 日期类对象之间的各种相互比较的运算符
- 日期类对象的自增、自减
- 获得日期类对象某年某月的有多少天
- 简单打印日志类时间
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);//其他的一些重载,TODO..//简单打印日期类时间void Print() const;//日期类之间比较的运算符bool operator<(const Date& x) const;bool operator==(const Date& x) const;bool operator<=(const Date& x) const;bool operator>(const Date& x) const;bool operator>=(const Date& x) const;bool operator!=(const Date& x) const;int GetMonthDay(int year, int month);// 日期类加天数运算符Date& operator+=(int day);Date operator+(int day) const;Date& operator-=(int day);Date operator-(int day) const;//日期类自增自减Date& operator++();Date operator++(int);Date& operator--();Date operator--(int);//日期类减日期类的天数差int operator-(const Date& d) const;
private:int _year;int _month;int _day;
};ostream& operator<<(ostream& out, const Date& d);
istream& operator>>(istream& in, Date& d);
我们实现的是一个简单的日期类,它的不涉及资源的管理,所以一些资源拷贝、复制、销毁的关键成员函数,像:析构、拷贝、赋值重载,我们都无需手动实现,而是交给编译器自动生成,自动生成的浅拷贝已经可以满足。
我们需要手动写的,就是特定的初始化方式,即构造函数,还有一些日期之间的常见运算对应的运算符重载。我们就采用类声明定义分离的方式去实现。
Date.h
#include<iostream>
#include<cassert>
using std::ostream;
using std::istream;
using std::cout;
using std::cin;
using std::endl;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);//其他的一些重载,TODO..//简单打印日期类时间void Print() const;//日期类之间比较的运算符bool operator<(const Date& x) const;bool operator==(const Date& x) const;bool operator<=(const Date& x) const;bool operator>(const Date& x) const;bool operator>=(const Date& x) const;bool operator!=(const Date& x) const;int GetMonthDay(int year, int month);// 日期类加天数运算符Date& operator+=(int day);Date operator+(int day) const;Date& operator-=(int day);Date operator-(int day) const;//日期类自增自减Date& operator++();Date operator++(int);Date& operator--();Date operator--(int);//日期类减日期类的天数差int operator-(const Date& d) const; //日期减日期也并不会修改对象本身的值,所以加上const禁止函数内部修改本身的值.
private:int _year;int _month;int _day;
};ostream& operator<<(ostream& out, const Date& d);
istream& operator>>(istream& in, Date& d);
Date.cpp
#include"Date.h"//实现一下构造出对象需求的基本成员函数.
Date::Date(int year, int month, int day)
{//首先我们需要判断日期的合法性.也因此,我们并不将赋初值的操作放在初始化列表.if ((month > 0 && month <= 12) && (day > 0 && day <= GetMonthDay(year, month))) //根据不同年份,2月的天数略有差距,所以写一个函数,更好的判断我们的日期天数,当给的数值合法,我们才正常的构造.{_year = year;_month = month;_day = day;}else{cout << "非法日期:" << "year=" << year << ", month=" << month << ", day=" << day << endl;assert(false);}
}int Date::GetMonthDay(int year, int month)
{int DayArr[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };int day = DayArr[month];if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)) //如果是2月并且是闰年,那么将天数加一.{day++;}return day;
}//早早写出打印的函数,可以阶段性的测试我们的代码正确性.
void Date::Print() const
{cout << "该对象数据:" << "_year=" << _year << ", _month=" << _month << ", _day=" << _day << endl;
}//2.然后先实现operator==和operator<,为什么?---后面的运算符重载可以复用逻辑.
bool Date::operator==(const Date& x) const //首先这个函数逻辑的内部不可以去改动内部的变量,其次我们的传入用作比较的Date对象也不可以被改动,所以均是const的.
{//if (x._day == _day && x._month == _month && x._year == _year)//{// return true;//}//return false;return x._day == _day && x._month == _month && x._year == _year;
}bool Date::operator<(const Date& x) const //同样,这个函数逻辑的内部不可以去改动内部的变量,其次我们的传入用作比较的Date对象也不可以被改动,所以均是const的.
{if (_year < x._year){return true;}else if(_year == x._year && _month < x._month){return true;}else if (_year == x._year && _month == x._month && _day < x._day){return true;}return false;
}//3.然后复用已经实现的逻辑,去实现其他的一些运算符重载.
bool Date::operator<=(const Date& x) const
{//if (*this < x || *this == x) return true;//return false;return *this < x || *this == x;
}bool Date::operator>(const Date& x) const
{return !(*this <= x);
}bool Date::operator>=(const Date& x) const
{return !(*this < x);
}bool Date::operator!=(const Date& x) const
{return !(*this == x);
}//4.然后实现日期类加天数的运算符重载函数
Date& Date::operator+=(int day)
{if (day < 0) //如果加一个负数,那么相当于减负数的绝对值.{return *this -= (-day);}_day += day;while(_day > GetMonthDay(_year, _month)){_day -= GetMonthDay(_year, _month);_month++;if (_month > 12){_month = 1;_year++;}}return *this; //为什么要返回它自己? -- 因为+=的逻辑本身就是自己去加.
}Date Date::operator+(int day) const
{Date tmp(*this); //这里是用拷贝构造去创建出一个临时的对象,至于为什么我们没有实现还有拷贝构造,因为浅拷贝使用编译器自动生成的就完全够用.tmp += day;return tmp; //这里又为什么要重新构造一个临时变量去返回? -- 因为+本来就是创造一个临时变量,像1 + 1,如果我们不用一个变量去接住它,那么答案2我们是拿不到的,//这种运算符重载的灵活体现,更加加深了我们类的抽象程度,使得其更加贴近内置类型.
}Date& Date::operator-=(int day)
{if (day < 0) //同样如果减等于一个负数,相当与加该负数的绝对值.{return *this += (-day);}_day -= day;while(_day <= 0){_month--;//_day += GetMonthDay(_year, _month); //放在这里是错的,因为有可能减到0,减到0,访问的日期就是0.if (_month == 0){_month = 12;_year--;}_day += GetMonthDay(_year, _month);}return *this;
}Date Date::operator-(int day) const
{Date tmp(*this);tmp -= day;return tmp;
}//5.然后实现一些自增自减运算符.
Date& Date::operator++()
{*this += 1;return *this; // 前置++表达式的返回值,是++之后的值,所以返回的是对象自己(引用).
}
Date Date::operator++(int) // 后置++,这个 int参数只用于编译器区分类型,不用于任何实际操作。
// 在函数内部,你不需要使用这个参数(通常省略其名字以避免未使用参数的警告)。编译器在遇到 obj++时,会自动传递一个 0(或其他任意整数值)给这个虚拟参数。
{Date tmp= *this;*this += 1;return tmp; //后置++的表达式返回值是++之前的值,所以我们返回一个临时对象.
}
Date& Date::operator--()
{*this -= 1;return *this;
}
Date Date::operator--(int)
{Date tmp = *this;*this -= 1;return tmp;
}//6.然后实现日期类减日期类的天数差
int Date::operator-(const Date& d) const
{Date max = *this;Date min = d;int flag = 1;if (*this < d){max = d;min = *this;flag = -1;}int n = 0;while (max != min){n++;min++;}return n * flag;
}//7.让我们的类支持<<和>>.
ostream& operator<<(ostream& out, const Date& d)
{out << "该对象数据:" << "_year=" << d._year << ", _month=" << d._month << ", _day=" << d._day << endl;return out; //为什么重载之后还需要返回out呢?因为有这种使用场景: cout << d << "你好兄弟"; 就相当于: (cout << d) << "你好兄弟";
}
istream& operator>>(istream& in, Date& d)
{int year, month, day;in >> year >> month >> day;if ((month > 0 && month <= 12) && (day > 0 && day <= d.GetMonthDay(year, month))) //根据不同年份,2月的天数略有差距,所以写一个函数,更好的判断我们的日期天数,当给的数值合法,我们才正常的构造.{d._year = year;d._month = month;d._day = day;}else{cout << "非法日期:" << "year=" << year << ", month=" << month << ", day=" << day << endl;assert(false);}return in;
}
一些测试代码:(main.cpp)
#include"Date.h"//测试构造函数的逻辑正确性
void test()
{Date d(1901, 12, 1);//Date d1(1901, 2, 29);Date d2(2000, 2, 29);Date d3(2000, 2, 29);d.Print();//d1.Print();d2.Print();}//测试operator==和operator<
void test1()
{Date d(1901, 12, 1);//Date d1(1901, 2, 29);Date d2(2000, 2, 29);Date d3(2000, 2, 29);cout << (d2 == d3);cout << (d2 < d3);cout << (d < d3);
}//测试其他运算符重载函数
void test2()
{Date d(1901, 12, 1);//Date d1(1901, 2, 29);Date d2(2000, 2, 29);Date d3(2000, 2, 29);cout << (d2 < d3);cout << (d2 <= d3);cout << (d > d3);cout << (d >= d3);cout << (d != d3);
}//测试我们日期类加天数的运算符重载函数
void test3()
{Date d(1901, 12, 1);d.Print();Date d1 = d + 100;d1.Print();Date d2 = d - 100;d2.Print();d.Print();d += 666;d.Print();d -= 666;d.Print();//d -= 654; //测试_year的逻辑对不对.//d.Print();//d -= 654;//d.Print();//d -= 654;//d.Print();
}//测试自增自减的功能
void test4()
{Date d(1901, 12, 1);d.Print();Date d1;d1.Print();d1 = d++;d1.Print();d1 = d--;d1.Print();d1 = ++d;d1.Print();d1 = --d;d1.Print();
}//测试日期类减日期类的天数差的功能.
void test5()
{Date d(1901, 12, 1);Date d1(2000, 2, 29);cout << d1 - d << endl;cout << d - d1 << endl;
}//测试我们类适配的cin和cout.
void test6()
{Date d(1901, 12, 1);cout << d;cin >> d;cout << d;//该对象数据:_year=1901, _month=12, _day=1//2000 12 25//该对象数据:_year = 2000, _month = 12, _day = 25
}int main()
{test6();return 0;
}
全文总结
实践简单类,知行合一有益身体健康。
本文章为作者的笔记和心得记录,顺便进行知识分享,有任何错误请评论指点:)。