前言
日期类是指处理日期和时间相关操作的编程类库或对象。它提供了创建、解析、比较、格式化日期和时间等功能,方便开发者在程序中处理与时间相关的逻辑。日期类通常包括年、月、日、时、分、秒等属性,并允许进行各种日期时间的计算和操作,如加减天数、获取星期几、判断是否为闰年等。通过使用日期类,开发者可以更加高效、准确地处理时间相关的数据。
一、日期类
本文的实现基于往期文章学过的内容
概念
日期类是一种用于表示日期的数据类型。它通常包含年、月、日等成员变量,以及一些用于操作日期的方法。
日期类可以用于记录和处理具体的日期信息,例如生日、纪念日、活动日期等。
日期类可以提供一些常用的功能,例如计算两个日期之间的时间差、判断一个日期是星期几、格式化日期等。
日期类还可以用于日期的比较和排序,通过比较日期对象的大小,可以判断哪个日期在前、哪个日期在后。
日期类在编程中广泛应用,特别是在计算机程序中需要处理时间和日期相关的业务逻辑时。
实现
代码语言:javascript复制#include"Date.h"
class Date
{
public:
// 获取某年某月的天数
int GetMonthDay(int year, int month)
{
static int days[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30,
31 };
int day = days[month];
if (month == 2
&& ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
{
day = 1;
}
return day;
}
// 全缺省的构造函数
Date(int year = 1900, int month = 1, int day = 1);
// 拷贝构造函数
// d2(d1)
Date(const Date& d);
// 赋值运算符重载
// d2 = d3 -> d2.operator=(&d2, d3)
Date& operator=(const Date& d);
// 析构函数
~Date();
// 日期 =天数
Date& operator =(int day);
// 日期 天数
Date operator (int day);
// 日期-天数
Date operator-(int day);
// 日期-=天数
Date& operator-=(int day);
// 前置
Date& operator ();
// 后置
Date operator (int);
// 后置--
Date operator--(int);
// 前置--
Date& operator--();
// >运算符重载
bool operator>(const Date& d);
// ==运算符重载
bool operator==(const Date& d);
// >=运算符重载
bool operator >= (const Date& d);
// <运算符重载
bool operator < (const Date& d);
// <=运算符重载
bool operator <= (const Date& d);
// !=运算符重载
bool operator != (const Date& d);
// 日期-日期 返回天数
int operator-(const Date& d);
private:
int _year;
int _month;
int _day;
};
运用场景
日期类的运用场景非常广泛,以下是一些常见的场景:
- 日历和时间管理:日期类可以用于创建日历和管理时间,例如在行事历应用程序中,可以使用日期类来跟踪和管理用户的日程安排。
- 数据处理和分析:在数据分析和处理的过程中,日期类可以用于对时间序列数据进行操作和计算,例如计算日期之间的时间间隔、按日期进行排序和过滤数据等。
- 事件调度和提醒:日期类可以用于事件调度和提醒的功能,例如在任务管理应用程序中,可以使用日期类来设置任务的截止日期,并提醒用户即将到期的任务。
- 日志记录和统计:日期类可以用于记录和统计事件的发生时间,例如在日志系统中,可以使用日期类来记录日志的时间戳,并对日志进行统计和分析。
- 计算器和时钟功能:日期类可以用于实现计算器和时钟功能,例如在计算器应用程序中,可以使用日期类来进行日期和时间的计算和显示。
总而言之,日期类的运用场景非常广泛,几乎在任何需要处理时间和日期的应用程序中都可以看到其身影。
二、日期类的具体实现代码
构造函数
代码语言:javascript复制Date(int year = 1900, int month = 1,int day = 1);
代码语言:javascript复制Date::Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
拷贝构造函数
代码语言:javascript复制Date(const Date& d);
代码语言:javascript复制Date::Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
获取日期(内联函数)
代码语言:javascript复制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 };
// 365天 5h
if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
{
return 29;
}
else
{
return monthDayArray[month];
}
}
函数的输入是年份和月份,都是整数类型。函数的输出是一个整数,表示指定月份的天数。
首先,函数使用assert
函数来确保传入的月份在有效范围内,即大于0
且小于13
。如果月份不在有效范围内,程序会终止。
然后,函数定义了一个静态的整型数组monthDayArray
,用于存储每个月份的天数。数组的下标对应月份,数组的值对应该月份的天数。
接下来,函数通过判断月份是否为2
月来处理闰年的情况。闰年的判断条件为:年份能被4
整除并且不能被100
整除,或者能被400
整除。如果是闰年,2
月的天数为29
天,否则使用数组monthDayArray
中对应月份的值作为天数。
最后,函数返回获取到的天数。
总结:这段代码是一个用于获取指定年份和月份的天数的函数,对闰年进行了处理,并使用数组存储了每个月份的天数。
赋值
代码语言:javascript复制Date& operator=(const Date& d);
代码语言:javascript复制Date& Date::operator=(const Date& d)
{
if (this != &d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
return *this;
}
这是一个重载赋值运算符的函数,用于将一个Date
对象赋值给另一个Date
对象。
首先,通过this
指针与待赋值对象(&d
)进行比较,确保不是自我赋值。
然后,将待赋值对象的私有成员变量(_year, _month, _day
)的值分别赋给当前对象的对应成员变量。
最后,返回当前对象的引用(*this
)。
这样,通过重载赋值运算符,可以实现Date
对象之间的赋值操作。
加等
代码语言:javascript复制Date& operator =(int day);
代码语言:javascript复制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 == 13)
{
_year;
_month = 1;
}
}
return *this;
}
这是一个重载" =
"运算符的函数,用于给当前的Date
对象增加指定的天数。
首先,检查增加的天数是否小于0
,如果是,则将其转换为正数,并使用递减运算符(-=
)来实现减少指定天数的操作,然后返回当前对象的引用。
接着,将给定的天数累加到当前对象的_day
成员变量上。
然后,使用一个while
循环来判断当前的_day
是否超过了当前月份的天数。如果超过了,就通过减去当前月份的天数来计算剩余的天数,并将_month
加1。如果_month
等于13(即当前月份是12月),则说明年份需要进位,将_year
加1,同时将_month
重置为1。
最后,返回当前对象的引用。
这样,通过重载" =
"运算符,可以实现给Date
对象增加指定天数的操作。
减等
代码语言:javascript复制Date& operator-=(int day);
代码语言:javascript复制Date& Date::operator-=(int day)
{
if (day < 0)
{
return *this = -day;
}
_day -= day;
while (_day <= 0)
{
--_month;
if (_month == 0)
{
_month = 12;
--_year;
}
int days = GetMonthDay(_year, _month);
_day = days;
}
return *this;
}
这是重载"-=
"运算符的函数,用于给当前的Date
对象减少指定的天数。
首先,检查减少的天数是否小于0,如果是,则将其转换为正数,并使用递增运算符( =
)来实现增加指定天数的操作,然后返回当前对象的引用。
接着,将给定的天数从当前对象的_day
成员变量中减去。
然后,使用一个while
循环来判断当前的_day
是否小于等于0。如果小于等于0,说明日期需要借位,所以将_month
减1。如果_month
等于0(即当前月份是1月),则说明年份需要借位,将_year
减1,同时将_month
重置为12。
接下来,根据减少的月份重新计算_day
的值。首先获取减少后的月份的天数,然后将_day
加上这个天数。
最后,返回当前对象的引用。
通过重载"-=
"运算符,可以实现给Date
对象减少指定天数的操作。
加
代码语言:javascript复制Date operator (int day);
代码语言:javascript复制Date Date::operator (int day)
{
Date tmp = *this;
tmp = day;
return tmp;
}
这是重载"
"运算符的函数,用于创建一个新的Date
对象,该对象的日期是当前Date
对象加上指定天数后的结果。
首先,创建一个临时的Date
对象tmp
,并将其初始化为当前对象的副本。
然后,使用重载的" =
"运算符将指定天数加到tmp
对象上。
最后,返回tmp
对象。
通过重载"
"运算符,可以实现给Date
对象加上指定天数后返回一个新的Date
对象的操作。
减
代码语言:javascript复制Date operator-(int day);
代码语言:javascript复制Date Date::operator-(int day)
{
Date tmp = *this;
tmp -= day;
return tmp;
}
这是重载"-
"运算符的函数,用于创建一个新的Date
对象,该对象的日期是当前Date
对象减去指定天数后的结果。
首先,创建一个临时的Date
对象tmp
,并将其初始化为当前对象的副本。
然后,使用重载的"-=
"运算符将指定天数从tmp
对象上减去。
最后,返回tmp
对象。
通过重载"-
"运算符,可以实现给Date
对象减去指定天数后返回一个新的Date
对象的操作。
小于
代码语言:javascript复制bool operator<(const Date& d)const;
代码语言:javascript复制bool Date::operator<(const Date& d)const
{
if (_year < d._year)
{
return true;
}
else if (_year == d._year)
{
if (_month < d._month)
{
return true;
}
else if (_month == d._month)
{
return _day < d._day;
}
}
return false;
}
这段代码是重载了"<
"运算符,用于比较两个日期对象的大小。
首先,比较当前对象的_year
和另一个日期对象d
的_year
。如果当前对象的_year
小于d
的_year
,则返回true
,表示当前对象的日期较早。
如果当前对象的_year
等于d
的_year
,则继续比较_month
。如果当前对象的_month
小于d
的_month
,则返回true
,表示当前对象的日期较早。
如果当前对象的_month
等于d
的_month
,则继续比较_day
。如果当前对象的_day
小于d
的_day
,则返回true
,表示当前对象的日期较早。
如果以上条件都不满足,则返回false
,表示当前对象的日期与d
相等或较晚。
通过重载"<
"运算符,可以方便地比较两个日期对象的大小。这在需要判断日期先后关系的场景中非常有用,比如排序、查找等操作。
小于等于
代码语言:javascript复制bool operator<=(const Date & d)const;
代码语言:javascript复制bool Date::operator<=(const Date& d)const
{
return *this < d || *this == d;
}
这段代码是重载了"<=
"运算符,用于比较两个日期对象的大小或相等关系。
首先,使用重载的"<
"运算符比较当前对象和另一个日期对象d
的大小。如果当前对象小于d
,则返回true
。
然后,使用重载的"==
"运算符比较当前对象和d是否相等。如果相等,则返回true
。
如果以上两个条件都不满足,则返回false
。
通过重载"<=
"运算符,可以方便地比较两个日期对象的大小或相等关系。这在需要判断日期先后关系和相等关系的场景中非常有用,比如进行条件判断、排序、查找等操作。
大于
代码语言:javascript复制bool operator>(const Date& d)const;
代码语言:javascript复制bool Date::operator>(const Date& d)const
{
return !(*this <= d);
}
这是一个重载的比较运算符(大于)的实现,用于比较两个Date
对象的大小关系。
该函数采用了常量引用参数const Date& d
,表示传递给该函数的参数d
是一个常量引用,即不会对d进行修改。这是为了保证在比较函数中不会对传入的对象进行修改。
函数的逻辑是先判断this
指针所指向的对象是否小于等于d
对象(使用小于等于运算符<=
),然后对这个结果取反,即得到大于运算符的结果。
函数实现中调用了<=
运算符,该运算符可能在Date
类中定义了,也可能是通过其他方式实现的。这段代码的逻辑是先判断两个Date
对象的大小关系(小于等于),再对结果取反,即得到大于运算符的结果。
大于等于
代码语言:javascript复制bool operator>=(const Date& d)const;
代码语言:javascript复制bool Date::operator>=(const Date& d)const
{
return !(*this < d);
}
这是一个重载的比较运算符(大于等于)的实现,用于比较两个Date
对象的大小关系。
该函数采用了常量引用参数const Date& d
,表示传递给该函数的参数d
是一个常量引用,即不会对d进行修改。这是为了保证在比较函数中不会对传入的对象进行修改。
函数的逻辑是先判断this
指针所指向的对象是否小于d
对象(使用小于运算符<
),然后对这个结果取反,即得到大于等于运算符的结果。
相等
代码语言:javascript复制bool operator==(const Date& d)const;
代码语言:javascript复制bool Date::operator==(const Date& d)const
{
return _year == d._year
&& _month == d._month
&& _day == d._day;
}
这是一个重载的相等运算符(等于)的实现,用于比较两个Date
对象是否相等。
该函数采用了常量引用参数const Date& d
,表示传递给该函数的参数d
是一个常量引用,即不会对d
进行修改。这是为了保证在比较函数中不会对传入的对象进行修改。
函数的逻辑是比较调用该函数的对象(即*this
)的_year
、_month
、_day
成员变量与参数对象d
的相应成员变量是否相等。如果这三个成员变量都相等,则返回true
;否则返回false
。
需要注意的是,由于该函数没有修改类中的成员变量,因此可以被声明为const
成员函数。使用const
关键字来修饰成员函数,表示该成员函数不会修改类的成员变量,也不会调用其他非const
成员函数,从而保证了常量对象也可以调用该函数。
不相等
代码语言:javascript复制bool operator!=(const Date& d)const;
代码语言:javascript复制bool Date::operator!=(const Date& d)const
{
return !(*this == d);
}
这是一个重载的不等运算符(不等于)的实现,用于比较两个Date
对象是否不相等。
该函数采用了常量引用参数const Date& d
,表示传递给该函数的参数d
是一个常量引用,即不会对d
进行修改。这是为了保证在比较函数中不会对传入的对象进行修改。
函数的逻辑是调用相等运算符(==
)来判断两个Date
对象是否相等。如果两个对象相等,则返回false
;否则返回true
。
需要注意的是,由于该函数没有修改类中的成员变量,因此可以被声明为const
成员函数。使用const
关键字来修饰成员函数,表示该成员函数不会修改类的成员变量,也不会调用其他非const
成员函数,从而保证了常量对象也可以调用该函数。
前置
代码语言:javascript复制Date& operator ();
代码语言:javascript复制Date& Date::operator ()
{
*this = 1;
return *this;
}
这是一个重载的前置递增运算符(
)的实现,用于对Date
对象进行自增操作。
该函数返回的是一个引用类型的Date
对象,即返回自增后的对象。这是为了实现连续的递增操作,例如: d1
, d2
, d3
。
函数的逻辑是先调用自定义的加法运算符( =
),将自身增加1天,然后返回自身引用。
需要注意的是,由于该函数会修改类的成员变量,因此不能被声明为const
成员函数。同时,返回引用类型的对象,可以通过连续使用操作符实现连续的自增操作。
后置
代码语言:javascript复制Date& operator (int);
代码语言:javascript复制Date& Date::operator (int)
{
Date tmp(*this);
*this = 1;
return *this;
}
这是一个重载的后置递增运算符(
)的实现,用于对Date
对象进行自增操作。
该函数返回的是一个引用类型的Date
对象,即返回自增前的对象。这是为了模拟后置递增操作符的行为,先返回旧值,然后再对对象自增。
函数的逻辑是先创建一个临时的Date
对象tmp
,将自身的值赋给tmp
。然后调用自定义的加法运算符( =
),将自身增加1天。最后返回tmp
。
需要注意的是,该函数的参数int
,只是为了区分前置递增运算符和后置递增运算符的函数签名,在函数体内并没有实际使用。同时,返回引用类型的对象,可以通过连续使用操作符实现连续的自增操作。
前置- -
代码语言:javascript复制Date& operator--();
代码语言:javascript复制Date& Date::operator--()
{
*this -= 1;
return *this;
}
这是一个重载的前置递减运算符(--
)的实现,用于对Date
对象进行自减操作。
该函数返回的是一个引用类型的Date
对象,即返回自减后的对象。这是为了模拟前置递减操作符的行为,先对对象自减,然后再返回新值。
函数的逻辑是调用自定义的减法运算符(-=
),将自身减少1天。然后返回自身。
需要注意的是,该函数没有参数,因为前置递减运算符不需要额外的参数来区分前置和后置形式。同时,返回引用类型的对象,可以通过连续使用操作符实现连续的自减操作。
后置- -
代码语言:javascript复制Date& operator--(int);
代码语言:javascript复制Date& Date::operator--(int)
{
Date tmp(*this);
*this -= 1;
return *this;
}
这是一个重载的后置递减运算符(--
)的实现,用于对Date
对象进行自减操作。
和前置递减运算符不同,后置递减运算符需要通过函数参数来区分前置和后置形式,参数的类型可以是int
或者一个无关的类型,这里使用int
作为参数。
函数的逻辑是先创建一个临时的Date
对象tmp
,将当前对象的值拷贝给tmp
。然后通过调用自定义的减法运算符(-=
),将自身减少1天。最后返回tmp
对象,也就是自减之前的值。
需要注意的是,返回的是一个临时对象的引用,而不是自身的引用。这是因为后置递减运算符需要返回自减之前的值,而不是自减之后的值。为了防止出现悬空引用的情况,使用临时对象来保存自减之前的值,并返回其引用。
关于类里重载的比较运算符为什么要加外部const
在C 中,比较运算符重载通常需要将其定义为成员函数。在重载比较运算符时,我们需要考虑到两个方面:
- 在比较运算符重载中,我们不希望修改类的成员变量。因此,我们需要将比较运算符定义为
const
成员函数。通过将成员函数标记为const
,我们告诉编译器这个函数不会修改类的任何成员变量。 - 我们还需要考虑到使用比较运算符的情况。在某些情况下,我们可能会在
const
对象上使用比较运算符。比如,如果我们将一个const
对象与另一个const
对象进行比较,我们需要确保比较运算符能够在const
对象上正确地进行比较。
因此,为了确保比较运算符能够在const
对象上正确地进行比较,并且不修改类的成员变量,我们需要将比较运算符定义为const
成员函数。这样可以保证比较运算符能够正确地在const
对象上使用,并且不会修改类的状态。
示例
代码语言:javascript复制const Date d1(2024,4,11);
d1< d2
会报错,是因为this
指针在类里是Date* const this
修饰的
在C 中,类里的成员函数有一个隐含的指向当前对象的指针,称为this
指针。在类的成员函数中使用this
指针,可以访问当前对象的成员变量和成员函数。
this
指针的类型是指向当前对象的非常量指针,因此this
指针默认情况下是指向非常量对象的。如果将一个常量对象传递给this
指针,即将一个常量对象赋值给非常量指针,就会导致类型不匹配的错误。
这是因为常量对象具有只读属性,不允许被修改,而非常量指针可以修改指向的对象。如果允许将常量对象传递给非常量指针,就会破坏常量对象的只读属性。为了确保对象的常量性,C 编译器会报错。
所以,类里的this
指针接受一个常量会报错。如果想在类的成员函数中操作常量对象,需要将成员函数声明为const
成员函数,在const
成员函数中,this
指针的类型会变为指向常量对象的指针。这样就可以在const
成员函数中访问常量对象的成员变量,但不能修改它们。
Date.h
代码语言:javascript复制#pragma once
#include<iostream>
#include<assert.h>
using namespace std;
class Date
{
public:
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 };
// 365天 5h
if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
{
return 29;
}
else
{
return monthDayArray[month];
}
}
Date(int year = 1900, int month = 1,int day = 1);
Date(const Date& d);
Date& operator=(const Date& d);
Date& operator =(int day);
Date& operator-=(int day);
Date operator (int day);
Date operator-(int day);
bool operator<(const Date& d)const;
bool operator<=(const Date & d)const;
bool operator>(const Date& d)const;
bool operator>=(const Date& d)const;
bool operator==(const Date& d)const;
bool operator!=(const Date& d)const;
Date& operator ();
Date& operator (int);
Date& operator--();
Date& operator--(int);
private:
int _year ;
int _month;
int _day;
};
Date.cpp
代码语言:javascript复制#include"Date.h"
Date::Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
Date::Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
Date& Date::operator=(const Date& d)
{
if (this != &d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
return *this;
}
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 == 13)
{
_year;
_month = 1;
}
}
return *this;
}
Date& Date::operator-=(int day)
{
if (day < 0)
{
return *this = -day;
}
_day -= day;
while (_day <= 0)
{
--_month;
if (_month == 0)
{
_month = 12;
--_year;
}
int days = GetMonthDay(_year, _month);
_day = days;
}
return *this;
}
Date Date::operator (int day)
{
Date tmp = *this;
tmp = day;
return tmp;
}
Date Date::operator-(int day)
{
Date tmp = *this;
tmp -= day;
return tmp;
}
bool Date::operator<(const Date& d)const
{
if (_year < d._year)
{
return true;
}
else if (_year == d._year)
{
if (_month < d._month)
{
return true;
}
else if (_month == d._month)
{
return _day < d._day;
}
}
return false;
}
// d1 <= d2
bool Date::operator<=(const Date& d)const
{
return *this < d || *this == d;
}
bool Date::operator>(const Date& d)const
{
return !(*this <= d);
}
bool Date::operator>=(const Date& d)const
{
return !(*this < d);
}
bool Date::operator==(const Date& d)const
{
return _year == d._year
&& _month == d._month
&& _day == d._day;
}
bool Date::operator!=(const Date& d)const
{
return !(*this == d);
}
//
Date& Date::operator ()
{
*this = 1;
return *this;
}
Date& Date::operator (int)
{
Date tmp(*this);
*this = 1;
return *this;
}
Date& Date::operator--()
{
*this -= 1;
return *this;
}
Date& Date::operator--(int)
{
Date tmp(*this);
*this -= 1;
return *this;
}