使用C++的知识点,练习一个日期类。
class Date
{
public:
//构造函数
Date(int year=1,int month=1,int day=1)
:_year(year)
,_month(month)
,_day(day)
{
assert(CheckDate());
}
//拷贝构造函数
Date(const Date& d)
{
_year=d._year;
_month=d._month;
_day=d._day;
}
void Print()
{
printf("%d年,%d月,%d日\n", _year, _month, _day);
}
private:
int _year;
int _month;
int _day;
};
构造函数设置了缺省值,变成默认构造函数,这里设置缺省值的时候还是要注意,日期肯定没有0月0日的,所以这里设置为了1。
//获取月份的天数
void GetMonthDay(int year,int month)
{
static int days[13]={0,31,30,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 days[month];
}
//检查日期是否正确 比如 2022,7,32
bool CheckDate()
{
if(_year>=1
&& _month>0 && month<13
&& _day>0 && _day<GetMonth(_year,_month)
)
{
return true;
}
else
{
return false;
}
}
检查月份的时候要检查一下闰年,如果是闰年二月有29天。
重载运算符
//日期加天数,天数超过一个月,进一月。超过一年进一年。
Date& operator+=(int day)
{
if( day < 0 ){
return *this -= -day; //这里是防止输入 d += -100 的情况
}
_day += day;
while(_day > GetMonthDay(_year,_month)){
_day -= GetMonthDay(_year,_month);
_month++;
if(_month == 13){
_year++;
_month = 1;
}
}
return *this;
}
//日期减天数,超过一个月则后退一个月,超出一年后退一年。
Date& operator-=(int day)
{
if( day < 0 ){
return *this += -day; //这里是防止输入 d -= -100 的情况
}
_day -= day;
while(_day <= 0){
_month--;
if(_month == 0){
_month=1;
_year--;
}
_day += GetMonthDay(_year,_month);
}
return *this;
}
这里刚开始的里面的if判断相互复用了,但是不会构成死循环,就是你调用我然后我再调用你的情况,运算符重载里面很多复用的情况,所以要避免相互调用的情况。
//前置-- 减后返回可以用引用
Date& operator--()
{
return *this -= 1;
}
//后置-- 先返回后相减
Date operator--(int)
{
Date temp = *this; //使用临时变量记录之前的值,所以这里不能返回引用
*this -= 1;
return temp;
}
Date& operator++()
{
return *this += 1;
}
Date operator++(int)
{
Date temp = *this;
*this += 1;
return temp;
}
这里要注意前置和后置的区分,用括号里面的int来区分的,有int则是后置。
然后要后置的意义是先返回后计算,所以用了一个临时变量来记录修改之前的值,所以这里不能返回引用,不然临时变量出函数销毁。
//重载赋值符号
Date& operator=(const Date& d)
{
if(this!=&d) //这里的& 是取地址的意思
{
_year=d._year;
_month=d._month;
_day=d._day;
}
return *this;
}
这里重载了赋值符号,要注意库里面的赋值符号是可以自己赋值给自己的,所以这里要判断一下,自己赋值自己遇到就跳过。
然后库里面的赋值是可以连续赋值的,所以这里就要返回对象的引用。
然后这里有个深浅拷贝的问题,但是这里类里面全是内置类型,所以使用浅拷贝也可以。
//比较符号的重载
bool operator>(const Date& d)
{
if((_year > d._year)
|| (_year == d._year && _month > d._month)
|| (_year == d._year && _month == d._month && _day > d._day)
)
{
return true;
}
else
{
return false;
}
}
bool operator==(const Date& d)
{
return _year==d._year
&& _month==d._month
&& _day==d._day;
}
bool operator!=(const Date& d)
{
return !(*this==d); //复用== 其结果取反
}
bool operator>=(const Date& d)
{
return (*this==d)||(*this>d);
}
bool operator<=(const Date& d)
{
return !(this>d);
}
bool operator<(const Date& d)
{
return !(*this>=d);
}
任何一个类只需要写一个>,和==重载(或者< ==) 剩下的比较运算符复用即可。但是要注意相互调用造成死循环的情况。
//日期减日期 获取相差的天数
int operator-(const Date& d)
{
int flag=1;
Date max=*this;
Date min=d;
if(*this<d){ //这里复用了小于号
max=d;
min=*this;
flag=-1;
}
int n=0;
while(max!=min){ //这里复用了不等于符号
min++;
n++;
}
return n*flag;
}
//函数重载 + 运算符重载 日期减天数
Date operator-(int day)
{
Date temp=*this;
temp -= day; //这里复用了减等于
return temp;
}
//重载加号不能改变对象本身
Date operator+(int day)
{
//直接复用+=
Date ret = *this;
ret += day;
return ret;
}
这里对减号重载了两次,分别对应不同参数的情况。
而且加号和减号跟加等于减等于不一样,其不会改变对象本身的值,这里就要使用一个临时变量返回,所以返回值不能用引用。
//重载流插入 流提取 ">>"、"<<"
friend ostream& operator<<(ostream& out,const Date& d);
//注意这要修改d的值,所以不能设置用const修饰,不然会报错
friend istream& operator>>(istream& in, Date& d);
ostream& operator<<(ostream& out, const Date& d)
{
out << d._year << "/" << d._month << "/" << d._day << endl;
return out;
}
istream& operator>>(istream& in, Date& d)
{
in >> d._year >> d._month >> d._day;
assert(d.CheckDate()); //检查输入的日期是否正确
return in;
}
这里的函数如果重载在类内部,使用的时候是 d<<cout ,实际上cout平时使用的时候是在左边的,所以这里要在类外面重载,然后为了能再类外部使用到类的私有变量,所以设置函数为友元函数。
并且为了能连续的打印和提取这里要返回流插入、流提取的引用。
这里要一个简易的日期类就完成了,但是要注意考虑现实一点的因素,比如运算符的重载要有意义,比如一个日期加上一个日期,是没意义的所以不用写。
然后就是在历史上有几天是被删除了的,所以太长的日期相减也是会出错误的,还有就是闰年并不是在公元元年被定义出来的,是过了后面好久。所以计算闰年的方法可能也要判断一下,这里只是练习C++的初步使用,所以不考虑太多。