因为类和对象有点复杂,一时间不知道怎么组织语言,所以先写这个。
---------
既然决定写一个 日期 类,那就需要三个参数:
年,月,日
所以先定义出来:
class Data
{
public:
private:
int _year;
int _month;
int _day;
};
里面的三个对象就不用公开,有需要可以直接在类里面操作。
------
我们知道,有三种构造函数:
1、无参
2、缺省
3、编译器自带
这里就不适合用编译器自带的,因为编译器自带的对于内置类型(int、double...)不会做处理,所以这里用的是缺省参数。
Data(int year,int month, int day)
{
this->_year = year;
this->_month = month;
this->_day = day;
}
为了方便阅读,所以我在这里会用this指针,可以简单理解为就是要改变的参数,假设这是d1要初始化,那this就是d1,this->_year = d1->year;
当写出这样的代码的时候,有没有一种可能,就是用户使用的时候他传来一个相对错误的日期,比方说:
不是闰年的时候传进来2 29号
一个月的第32天
公元300年
...
显然,这些是我们没有考虑到的,所以为了更合理的使用这个日期类,我们最好加一个判断条件:
如果年份太离谱,报错
如果月份日期不对,报错
但是,要如何来判断这个月份对不对呢?
可以再套一个函数:
Data(int year,int month, int day)
{
if (year < 1900 ||
month < 1 ||
month>12 ||
day>GetMonthDay(year, month))
{
cout << "error" << endl;
}
this->_year = year;
this->_month = month;
this->_day = day;
}
稍微改进一下,如果年份小于1900,那就报错,如果月份小于1,如果月份大于12,或者是
day>GetMonthDay ;
这个GetMonthDay ,是之后要实现的一个函数,来判断这个月有多少天,具体实现也挺简单的,首先要开辟一个数组,这个数组里面放上每个月的具体天数,然后根据传过去的参数来返回这个月有多少天。
这里要注意一点,别忘了判断闰年:
int GetMonthDay(int year, int month)
{
int MonthDayArray[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
if (year % 4 == 0 && year % 100 != 0 ||
year % 400 == 0)
if (month == 2)
return 29;
return MonthDayArray[month];
}
这里要注意的一点是,最后返回日期的时候,如果创建的数组的大小是12,那返还的的应该是MonthDayArray[month-1]
连起来:
既然写好了构造函数,那就来用一下:
报错,因为2022不是闰年,所以没有29天
既然创建好了一个初始对象,那我们正好写一个打印函数,来方便我们查看里面的内容:
void printf()
{
cout << this->_year << " " << this->_month << " " << this->_day << endl;
}
很简单的函数,就不细说了。
可以看到,如果是之前的代码来跑这个的话,因为当时写的时候是直接打印error然后返回,所以d1里面的值都是随机值,如果我们写一个正确的值的话:
还是没问题的。
-----
写不写都可以,这里为了说明写一个简单一点的析构函数:
~Data()
{
this->_day = this->_month = this->_year = 0;
}
类名前面加上一个波浪号表示这是一个析构函数,实现方式就是直接讲三个参数重制为零就好了。
-------
初始化的时候,我们不止可以用单纯的的类型来初始化,还可以用一个对象来初始化另一个对象,这个操作称之为拷贝构造。
拷贝分为两种:
浅拷贝,也称之为值拷贝,它是很简单的一个字节一个字节的拷贝
对于那些int double 来说它是可以用的,但是假若开辟了一块空间,那浅拷贝的时候它会把这个指针的地址也拷贝过去,也就是说两个对象的指针指向的是同一块空间,就会出现析构两次,或者是操作一个对象另一个对象也出现对应现象的错误。
为了解决这种错误,出现了另一个,叫做:深拷贝
深拷贝就不会出现这种问题,但对于我们现在写的日期来说,浅拷贝也已经够用了。
同样,这里也是可以不写的,这里是为了示范:
Data(const Data& d)
{
this->_year = d._year;
this->_month = d._month;
this->_day = d._day;
}
这里要说一点,当我们传参数的时候,假若是要讲d2拷贝为d1的内容,那我们调用类函数的时候是这样的:
d2(d1);
我们传过去的参数只要一个d1,d2它是会被默认传过去的,如果在传一个d2过去就会引发错误,而我们想找d2的时候直接用this就可以了。
所以这里直接就是this->_year = d._year
--------
这个就好像那些很简单的,用一个对象来赋值给另一个类型的对象,注意以下几点:
注意传参的时候只用传要赋值的那个就可以了
注意被赋值的对象跟要赋值的对象应该不一样,如果是自己赋值自己,这种行为还是报错比较好
注意我们最后要返回这个值本身,因为语言本身支持这样:a = b = c,这种连续赋值,如果我们不返回值那这种情况就会报错
注意用引用,如果返回的单纯是一个值,返回中会有拷贝过程,所以这里用引用效率,或者说是速度会更快一点。
注意this是一个对象指针,指向的是要赋值的那个对象,所以要返回对象的时候应该先解引用。
Data& operator=(const Data& d)
{
if (this == &d)
cout << "error" << endl;
else
{
this->_year = d._year;
this->_month = d._month;
this->_day = d._day;
}
return *this;
}
这个其实也很好解决,我们直接将天数加上去,然后进行判断,如果这个天数大于这个月该有的天数,那我们的月份就+1,然后天数-这个月的天数,如果过程中月份到了13,那就应该把月份重制为1.
简单写一个:
Data& operator+=(int day)
{
this->_day += day;
while (this->_day > GetMonthDay(this->_year, this->_month))
{
this->_day -= GetMonthDay(this->_year, this->_month);
this->_month += 1;
if (this->_month == 13)
{
this->_year += 1;
this->_month = 1;
}
}
return *this;
}
目前效果:
既然有+=,那就有+,不过这里有一点要注意:
+不一定是真的+上去,也许是在表达式里面进行的一个判断过程,所以我们不能直接用+=的思想。
这里也简单写一个:
Data& operator+(int day)
{
Data tmp(*this);
tmp += day;
return tmp;
}
先初始画一个拷贝的值,然后对这个拷贝的值进行操作,再返回,不要对*this操作,注意注意注意。
-------
有加就有减,大体思路其实跟日期+差不多,同样是先减,然后再循环判断:
注意,先减一月,因为在上来的this->_day -= day的时候这个月已经减完了,所以要拿下个月的出来。
Data& operator-=(int day)
{
this->_day -= day;
while (this->_day < 0)
{
this->_month -= 1;
if (this->_month == 0)
{
this->_year--;
}
this->_day += GetMonthDay(this->_year, this->_month);}
return *this;
}
-------
这个,就没什么好说的,如果明白了+,那-应该也明白了。
Data& operator-(int day)
{
Data tmp(*this);
tmp -= (day);
return tmp;
}
------
作为一个日期类,比较大小还是很简单的,首先比较年,如果年比较不出来大小,那就比较月,如果月比较不出来,那就比较天。
简单写一下:
bool operator>(const Data& d)
{
if (this->_year < d._year ||
this->_year <= d._year&&this->_month < d._month ||
this->_year <= d._year&&this->_month <= d._month&&this->_day <= d._day)
return false;
return true;
}
效果:
---------
这个,算非常简单的那一种,我们直接比较对应的年月日即可:
bool operator==(const Data& d)
{
return this->_year == d._year&&
this->_month == d._month&&
this->_day == d._day;
}
效果:
-----
这里,>=的结果,是不是就是把我们之前写的大于和等于结合起来。
所以我们直接复用代码:
bool operator>=(const Data& d)
{
return *this > d||*this == d;
}
效果:
------
小于,是不是大于等于的结果取反?
所以还是直接复用代码:
bool operator<(const Data& d)
{
return !(*this >= d);
}
效果:
-------
小于等于,那不就是大于的取反吗
继续复用:
bool operator<=(const Data& d)
{
return !(*this>d);
}
效果:
--------
这还用说吗,等于取反:
bool operator!=(const Data&d)
{
return !(*this == d);
}
效果:
-----
这里有好几种方法实现,比如说:
1、年减年,月减月,日减日
2、一直加那个小的直到它们两个相等
第一种方法是很容易想到但是不容易实现的,因为要考虑到有的年月天数不一样,所以会比较麻烦,这里推荐使用第二种:
因为不能改变原有数据,所以我们先创建一个对象,让它来表示小的那个,然后循环:
int operator-(const Data& d)
{
Data min(*this);
Data max(d);
int count = 0;
if (*this > d)
{
min = d;
max = *this;
}
while (max != min)
{
count++;
min+=1;
}
return count;
}
默认小的那个是*this,大的那个是d,然后判断如果取反了就交换,再累加计算,效果:
-----
这里要注意的一点是,前置++是先++,然后再算。
Data operator++()
{
*this += 1;
return *this;
}
效果:
----
这里要注意一点,因为是后置,所以为了区分前置,它会在参数里面默认传过去一个int,不需要接收,只是为了表明这是后置++。
注意一点,后置++是先运行完再++,所以不能直接用*this,而是用一个对象保存当前*this的值饭后,然后再++*this。
Data operator++(int)
{
Data tmp(*this);
*this += 1;
return tmp;
}
可以看到第一次的时候还不相等,++完就相等了。
-----
跟前置++一样,直接复制过来修改一下就好:
Data operator--()
{
*this -= 1;
return *this;
}
同理:
Data operator--(int)
{
Data tmp(*this);
*this -= 1;
return tmp;
}
---------
支持,日期类就告一段落,希望这篇文章对您有所帮助,下篇文章再见