1.日期类的实现
(1)全缺省构造函数、拷贝构造函数、赋值运算符重载函数、析构函数的实现
cpp
public:
Date(int year = 1900, int month = 1, int day = 1)
{
if (year < 1900|| month < 1 || month > 12
|| day < 1 || day > GetMonthDay(year, month))
{
cout << "非法日期" << endl;
}
_year = year;
_month = month;
_day = day;
}
Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
Date& operator=(const Date& d)
{
if (this != &d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
return *this;
}
~Date()
{
;
}
事实上,拷贝构造函数、赋值运算符重载函数和析构函数不需要自己去实现,因为Date类的成员变量全是内置类型,没有额外开辟空间,所以系统默认生成的函数就足够了。但这里的全缺省构造函数是根据日期类的特性写的,为了保证日期类的初始化合法,需要通过if语句加上一些限制。
(2)+=、+、-=、-(参数为天数)的实现
在实现这些函数之前,仔细思考实现的过程,会发现最大的难点其实是获取各年各月的天数,所以在此之前,需要先写一个GetMonthDay函数:
cpp
int GetMonthDay(int year, int month)
{
assert(month > 0 && month < 13);
static int monthDayArray[13] = { -1,31,28,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 monthDayArray[month];
}
++static++ int monthDayArray[13] = { -1,31,28,31,30,31,30,31,31,30,31,30,31 };
这里加一个static把数组放到静态区,因为这个函数必然会被频繁地调用,这样每次调用就不用重新创建数组了
if (++month == 2 &&((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))++)可见这个判断可以分为两个,一个是++month == 2++ ,另一个是++(year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)++,明显前一个比后一个简单,所以把简单的放在前面,可以提高运行的效率
GetMonthDay不做声明和定义分离,因为这个函数必定会被频繁地调用,直接定义在类里面,则默认为内联函数,调用时直接展开,避免建立函数栈帧,提高效率。有了GetMonthDay函数,+=的实现就不难了,实现逻辑如下:

cpp
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)
{
_year++;
_month = 1;
}
}
return *this;
}
有了+=,+的实现其实可以通过直接复用+=函数来实现:
cpp
Date Date::operator+(int day)
{
Date d1 = *this;
d1 += day;
return d1;
}
这给我们提供了一个新思路,是否可以先实现+再复用+实现+=呢?也是可以的,代码如下:
cpp
Date& Date::operator+=(int day)
{
*this=*this+day;
return *this;
}
Date Date::operator+(int day)
{
Date d1 = *this;
d1._day += day;
while (d1._day > GetMonthDay(d1._year, d1._month))
{
d1._day -= GetMonthDay(d1._year, d1._month);
d1._month++;
if (d1._month > 12)
{
d1._year++;
d1._month = 1;
}
}
return d1;
}
*this++=++*this+day;
这里用到了赋值操作符重载函数
现在来讨论"1.先实现+=再复用+=实现+"与"2.先实现+再复用+实现+="这两种方法哪种更优:观察代码可知第一种方法中+=没有拷贝,+拷贝了两次
第二种方法中+拷贝了两次,+=拷贝了三次
![]()
所以,总的来说1.先实现+=再复用+=实现+这种方法更好,效率更高。
再来看-、-=的实现,按照上面的分析,先来实现-=。
-=的实现逻辑和+=不同,代码如下:
cpp
Date& Date::operator-=(int day)
{
if (day < 0)
{
return *this += -day;
}
_day -= day;
while (_day <= 0)
{
if (_month == 1)
{
_year--;
_month = 13;
}
_day += GetMonthDay(_year, --_month);
}
return *this;
}
-通过复用-=来实现,代码如下:
cpp
Date Date::operator-(int day)
{
Date d1=*this;
return d1 -= day;
}
测试及结果:

(3)前置++、后置++、前置--、后置--的实现
前置++、后置++、前置--、后置--的实现并不难,只需要分清前置和后置的概念即可,前置则返回++后的类,后置则返回++前的类,但无论是后置还是前置,类都经过了+=1的过程;
cpp
Date& Date::operator++()
{
*this+=1;
return *this;
}
Date Date::operator++(int)
{
Date d1 = *this;
*this += 1;
return d1;
}
Date& Date::operator--()
{
*this -= 1;
return *this;
}
Date Date::operator--(int)
{
Date d1 = *this;
*this -= 1;
return d1;
}
测试及结果:

(4)<、<=、>、>=、==、!=的实现
这一组函数也不需要每一个都实现,观察发现,只要实现了<和==,其他的函数都可以通过函数复用来实现。
先来看<的实现,两个日期之间作比较需要比较年、月、和日,如果逐个比较过程会非常繁琐,但如果只把<的情况筛选出来并返回true,其他的全部返回false,则会好写很多,代码如下:
cpp
bool Date::operator<(const Date& d)
{
if (_year < d._year)
{
return true;
}
else if (_year == d._year && _month < d._month)
{
return true;
}
else if (_year == d._year && _month == d._month && _day < d._day)
{
return true;
}
else
return false;
}
其他函数的实现如下:
cpp
bool Date::operator==(const Date& d)
{
return _year == d._year && _month == d._month && _day == d._day;
}
bool Date::operator<=(const Date& d)
{
return *this < d || *this == d;
}
bool Date::operator>(const Date& d)
{
return !(*this <= d);
}
bool Date::operator>=(const Date& d)
{
return !(*this < d);
}
bool Date::operator!=(const Date& d)
{
return !(*this == d);
}
测试及结果:

(5)-(参数为日期类)的实现
以下是一个较简单的实现方法,先判断两个日期类的大小,再让小的日期不断++,直到等于大的日期,在这个过程中计数,++了多少次就相隔了多少天。
cpp
int Date::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 (min != max)
{
min++;
n++;
}
return n * flag;
}
测试及结果:

2.取地址运算符重载
(1)const成员函数
将const修饰的成员函数称为const成员函数,const修饰成员函数放到成员函数参数列表的后面。
cpp
void Print() const
{
cout << _year << "-" << _month << "-" << _day << endl;
}
const实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改,所以只有不需要改变自身的函数才能加const。

const 修饰Date类的Print成员函数后,Print隐含的this指针变为const Date* const this,缩小了权限,这个时候调用:
int main()
{
Date d1(2026, 1, 31);//对非const对象来说权限缩小了
d1.Print();
const Date d2(2026, 1, 31);//对const对象来说权限平移
d2.Print();
return 0;
}
(2)取地址运算符重载
取地址运算符重载分为普通取地址运算符重载和const取地址运算符重载,一般这两个函数编译器自动生成的就足够我们用了,不需要去显式实现。
cpp
class Date
{
public:
Date* operator&()
{
return this;
// return nullptr;
}
const Date* operator&()const
{
return this;
// return nullptr;
}
private:
int _year;
int _month;
int _day;
};

