1.定义一个日期类
有关类的定义,首先是需要声明共有类和私有类的成员函数和成员变量。
这里我分了三个文件写,分别有Date.h,Date.cpp,test.cpp
using namespace std;
class Date
{
private:
int _year;
int _month;
int _day;
public:
Date(int year, int month, int day);
void Print()
{
cout << _year << "/" << _month << "/" << _day << endl;
}
};
在代码中,声明了一个Date的构造函数,但是我把Date的定义放到了Date.cpp里面,也就是说,这里的声明和定义分离了。为什么要这样搞?这是由于如果不进行声明与定义的分离的话,当Date.cpp和test.cpp两个文件共同包含了Date.h的时候,在编译后进行链接的时候,由于会出现两个Date的构造函数,会被编译器认为是一种重定义的函数,会出现链接错误。因此这边就把他们的声明和定义分离了。
那为什么声明和定义分离就不会有链接错误的情况出现呢?这就得好好说道说道什么是声明,什么是定义了。
在以往中,我们会经常喜欢把写一个int add(int x , int y);说成是函数的定义,其实这种说法是不正确的,在C++中,这仅仅是函数的声明,声明是不带有任何性质的,仅仅是让编译器看一眼,知道有这种东西的存在。就好比,我口头答应了要借给你100块钱,但我还没有给你100块钱,这就是声明。那什么是定义?定义就是写好了这个函数实现的方法。还是以上面的int add(int x , int y);为例子,我仅仅是把函数名写出来的时候他就是声明,但是我把他的实现方法写出来呢?
int add(int x ,int y)
{
return x + y;
}
这种函数实现的过程就叫做定义。在这种情况下,函数就有了他自己的实际的意义。
2.对运算符进行重载
在实现日期类中,我们会涉及到日期的比较,和一些计算。
在进行日期类加减的时候,为了方便使用,会对运算符进行重载。
1.重载<号
那首先为了后续代码的方便,先重定义一下小于号。
bool Date:: operator<(const Date& date)
{
if (this->_year < date._year)
{
return true;
}
else if (this->_year == date._year)
{
if (this->_month < date._month)
{
return true;
}
else if (this->_month == date._month)
{
if (this->_month < date._day)
{
return true;
}
}
}
return false;
}
可以看到,我在传参的时候,传了一个const Date& date这样的一个值。那为什么要写成这样?
这就涉及到了拷贝构造函数的调用。其实这中就是传一个自定义类型的参数,传引用传参和传值传参有什么区别吗?
在C++中,对于自定义类型的传参分为两种类型,一种是传值传参,一种是传引用传参。
传值传参是把自定义类型的值传到函数体里面,函数体并不是直接对他进行操作,而是首先把传过来的自定义类型进行一个拷贝,创建副本,然后再对副本进行操作,这样的操作方式是不改变参数本身的。
而传引用传参的话,就直接对传过来的参数进行操作,是会改变参数本身,就类似于之前的Swap函数的参数,参数是否是指针类型,如果是,那么就可以直接交换,如果不是,那就只能是在哪个函数域里面进行交换,出了函数域,就不会进行交换。
对于该函数的逻辑,这边再简单的说一下。首先是明确是谁在跟谁比较,如果我创建一个类是d1,另一个日期类是d2,d1<d2,这样的表达式。那么就是d1与d2在比较,而d1在函数中,他的值就存在于函数所隐含的this指针。我们传过去的d2,就是写进待传的参数里面。
2.重载>=,==,<=等运算符
前面的小于号我们已经重载好了,这就打下了一个非常良好的基础,我们甚至可以利用<号来进行接下来的符号重载。
既然是首先重载>=,那么我们不妨直接对<的逻辑进行取反不就可以了吗。
bool Date:: operator>=(const Date& date)
{
return (*this < date);
}
其它的运算符重载也是以此类推,虽然说看起来重载了这么多,实则能用上的不过就是>,<,==这三个符号,其它的不过是为这三个符号铺路。
bool Date:: operator>=(const Date& date)
{
return !(*this < date);
}
bool Date::operator==(const Date& date)
{
return this->_year == date._year && this->_month == date._month && this->_day == date._day;
}
bool Date::operator<=(const Date& date)
{
return (*this < date) || (*this == date);
}
bool Date::operator>(const Date& date)
{
return !(*this <= date);
}
bool Date::operator!=(const Date& date)
{
return !(*this == date);
}
3.对+=,+,-=,-,++运算符进行重载
首先对+=,+号进行重载。目的是用当前的日期加上天数,最后输出加上天数后的日期。
Date& Date:: operator+=(int day)
{
this->_day += day;
while(this->_day > GetMonthDay(this->_year,this-> _month))
{
this->_day -= GetMonthDay(this->_year, this->_month);
this->_month++;
if (this->_month > 12)
{
this->_year++;
this->_month = 1;
}
}
return *this;
}
Date Date:: operator+(int day)
{
Date temp = *this;
temp += day;
return temp;
}
这个重载+=代码的整体思路就是,一上来就先把日期加起来,如果加起来的日期超过当前日期的月份(比如在这里计算的是2024年1月1日+100天,如果加起来的天数超过1月份的天数),那么首先就把总天数-当前月份的最大天数,其次月份进行++,由于判断当前月份的天数比较复杂,且2月份又得区分当前年份是否是闰年,因此写了个GetMonthDay函数,这个稍后会解释。接着上面,当月份++后,如果月份>12,则年就++,把月份设置成1。
而在重载运算符+号的时候,可以创建一个空类,然后对这个空类进行操作。
接下来是对-=和-进行重载。
在重载-的时候,我发现我在这里想实现的是日期减去天数,但如果重载的时候使用了-号的话,那么后面的日期-日期就会冲突,因此就重载/来实现日期-天数,至于-号就用来实现日期-日期吧。
Date & Date:: operator-=(int day)
{
this->_day -= day;
while (this->_day <= 0)
{
this->_month--;
if (this->_month <= 0)
{
this->_year--;
this->_month = 12;
}
this->_day += GetMonthDay(this->_year, this->_month);
}
return *this;
}
Date Date:: operator/(int day)
{
Date temp = *this;
*this -= day;
return temp;
}
int Date:: operator-(const Date& date)
{
Date max = *this;
Date min = date;
if (*this < date)
{
max = date;
min = *this;
}
int n = 0;
while (min != max)
{
min++;
n++;
}
return n;
}
前两个的思路都挺简单的,都是跟+=类似。我只最后说说最后的那个代码的思路吧。
首先,这个思路虽然方便,但是造成了较多的损耗。
先定义max和min,然后进行比较,如果最大值和最小值不匹配则max和min进行交换。
然后,让min一直进行自加操作,不断向着max逼近,创建一个count变量来记录,miin自加多少,count就加多少。最后返回的count就是两个日期类之间相差的天数。