您现在的位置是:首页 >技术教程 >如何实现一个完整的日期类?网站首页技术教程
如何实现一个完整的日期类?
文章目录
?前言
通过类和对象的基本学习,我们可以实现一个完整的日期类。本文探讨日期类如何实现。
Tip
关于内联函数:在类内定义的函数,如果代码量少的话,编译器会酌将其转换成内联函数,这样会在调用的地方直接展开,能够提高效率。
在这个日期类中,提高的效率不是很大,所以在本文将日期类的成员函数的声明和定义分离了。如果想写成内联,需要直接在类里面定义。(内联的声明和定义不能分离)
日期类的六大默认成员函数:
日期类的成员变量如下:
class
{
private:
int _year;
int _month
int _day;
};
一、?构造函数
由于日期类的成员变量只有年月日,且无其他的动态资源的申请,所以构造函数可直接实现:
// 获取某年某月的天数
int Date::GetMonthDay(int year, int month)
{
if (year < 0 || year>9999)
{
perror("error year!");
exit(-1);
}
if (month < 0 || month > 12)
{
perror("error month!");
exit(-1);
}
static int DaysGet[13] = { 0,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;
}
else
{
return DaysGet[month];
}
}
//构造函数
Date::Date(int year = 1900, int month = 1, int day = 1)
{
if (year < 0 || year>9999 )
{
perror("error year!");
exit(-1);
}
if (month < 0 || month > 12)
{
perror("error month!");
exit(-1);
}
if (day < 0 || day > GetMonthDay(year, month))
{
perror("error day!");
exit(-1);
}
_year = year;
_month = month;
_day = day;
}
Tip:
GetMonthDay()
函数是获取某年某月的天数,比如2023年5月,返回31天。
细节点1.
GteMonthDay的数组写成static是为了提高效率,因为GetMonthDay函数需要不断地调用,所以这个数组只需要创建一次即可,不需要重复创建。
细节点2.
在判断闰年部分,最开始先要判断月份是否为2月,(因为闰年只跟2月有关)再判断是否为闰年即可提高效率。
二、?拷贝构造函数
日期类不需要动态申请空间资源,所以这里的拷贝构造函数可写可不写,因为如果我们不写,编译器生成的拷贝构造函数会自己完成内置类型的浅拷贝。
Date::Date(const Date& d)
{
this->_year = d._year;
this->_month = d._month;
this->_day = d._day;
}
三、?析构函数
由于日期类没有动态资源空间的申请,不需要实现析构函数。
四、?日期类的赋值运算符重载
Date& Date::operator=(const Date& d)
{
//防止自己给自己赋值,可以判断一下
//注意&d的&是取地址的意思
if (this != &d)
{
this->_year = d._year;
this->_month = d._month;
this->_day = d._day;
}
return *this;
}
Tip:
1.赋值运算符重载的返回类型是Date&,为了达到连续赋值的效果,且能提高效率。
2.参数设置为const Date&,是因为赋值前后右值未发生改变,使用const修饰更安全,且传引用可以提高效率,减少调用构造函数或拷贝构造函数的次数。
五、?取地址运算符重载
返回对象的地址即可。(在日期类没有特别大的意义)
const Date* Date::operator&()
{
return this;
}
六、?const取地址运算符重载
返回对象的地址即可。(在日期类没有特别大的意义)
const Date* Date::operator&() const
{
return this;
}
Tip:
取地址运算符重载和const取地址运算符重载区别在于this指针是否被const修饰,
一个是 Datethis, 一个是 const Datethis,它们构成函数重载。
日期类的关系操作符重载
在日期类中,只要实现
1.>运算符重载和==运算符重载
或者
2.<运算符重载和==运算符重载
即可完成其他的关系运算符重载。
一、?>运算符重载
1.先判断年是否大于
2.如果年大于,再判断是否月大于
3.如果年和月都大于,再判断日是否大于
以上三种情况均返回真,其余情况返回假
bool Date::operator>(const Date& d)
{
if (_year > d._year)
{
return true;
}
if (_year == d._year && (_month > d._month))
{
return true;
}
if ( (_year == d._year) && (_month > d._month) && (_day > d._day) )
{
return true;
}
return false;
}
二、?==运算符重载
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);
}
日期类和天数的操作
一、?日期+=天数
日期+=天数,就是让日期本身变了。
比如:2023年1月1日+10天后,原来的日期变成了2023年1月11日。
返回原来的对象,但是对象的成员变量已被修改。
具体实现:
给定一个天数,首先让对象的天数加上,如果对象的天数大于当月的天数,就让对象的天数给减掉当月的天数,然后让月份进一位,然后判断月份是否大于12,如果月份大于12,让年进一位,同时让月份回到1月,如此循环。
最后返回*this,即返回自身。
//+=就是让自己变了
Date& Date::operator+=(int day)
{
//如果天数<0,意思就是
if (day < 0)
{
return *this -= -day;
}
_day += day;
while (_day > GetMonthDay(_year, _month))
{
_day -= GetMonthDay(_year, _month);
_month += 1;
if (_month > 12)
{
_year += 1;
_month = 1;
}
}
return *this;
}
Tip:
需要注意的点:如果日期小于0,相当于+=一个小于0的数,即 -= 一个大于0的数,可以复用下面实现的-=运算符重载。
二、?日期+天数
直接复用+=即可。
但是要注意的是,日期+天数返回的是一个新的对象,不是返回原来的对象。
所以需要在函数内部新建一个对象,该新的对象调用它拷贝构造函数拷贝原来的对象,+=天数后返回新对象。
Date Date::operator+(int day)
{
//可以复用 +=
Date tmp(*this);
tmp.operator+=(day);
return tmp;
}
三、?日期-=天数
整体实现方法与+=类似,但需要注意的是:
-=时,天数应该与上一个月的天数进行比较,因为-的是上个月的天数。
首先将对象的日期减掉天数,如果对象日期小于0,那就需要从上一个月来借用天数了。
回到上一个月先需要–_month,才可以回到上个月,–对象的月份后,如果月份小于1了,就需要从年借一年,–对象的年,然后让月份 = 12,如此循环。
Date& Date::operator-=(int day)
{
if (day < 0)
{
return *this += -day;
}
_day -= day;
while (_day < 1)
{
--_month;
if (_month < 1)
{
--_year;
_month = 12;
}
_day += GetMonthDay(_year, _month);
}
return *this;
}
Tip:
需要注意的点:如果日期小于0,相当于-=一个小于0的数,即 +=一个大于0的数,可以复用+=运算符重载。
四、?日期-天数
复用-=即可,与日期+天数类似,返回的是一个新的对象,不改变原对象。
// 日期-天数
Date Date::operator-(int day)
{
//可以复用 -=
Date tmp(*this);
tmp.operator-=(day);
return tmp;
}
五、?日期-日期
日期-日期的思想在于,先假设被减数是大的日期,减数是小的日期,如果假设不成立,那就交换一下顺序。
如果是小-大,那么返回日期的负数,表示回退多少多少天。
计算天数方法:从小的日期开始,不断累加,直到加到和大的日期相等即可。
在此期间会调用++运算符重载,在下面会实现。
//日期-日期
int Date::operator-(const Date& d)
{
//小-大返回负数,大-小返回正数
Date max = *this;
Date min = d;
int flag = 1;
//如果假设错误,交换一下
if (max < min)
{
max = d;
min = *this;
flag = -1;
}
int days = 0;//计算天数差
while (min != max) //!=更高效, <还要比年月日
{
++days;
++min;
}
return flag * days;
}
日期类++和–操作
一、?前置++
复用+=运算符重载即可。
Date& Date::operator++()
{
//this->operator+=(1);
*this += 1;
return *this;
}
二、?后置++
注意前置++和后置++是构成函数重载的,为了区分,我们给后置++一个参数类型,该参数不需要用到,所以给不给参数名都可以。
一般来说,尽量使用前置++,因为后置++返回的是++之前的对象,需要新建立一个对象,返回再返回该对象,新建立对象会调用拷贝构造函数,返回该对象又会给临时空间调用拷贝构造函数,效率较低。
Date Date::operator++(int)
{
Date tmp(*this);
//this->operator+=(1);
tmp += 1;
return tmp;
}
三、?前置–
–操作的解释与++操作相似。
// 前置--
Date& Date::operator--()
{
//this->operator-=(1);
*this -= 1;
return *this;
}
四、?后置–
–操作的解释与++操作相似。
Date Date::operator--(int)
{
Date tmp(*this);
//tmp.operator-=(1);
tmp -= 1;
return tmp;
}
整体代码(Date.h 和Date.cpp)
Date.h文件
class Date
{
//成员函数如果不放在public里面,默认就是私有的
public:
// 全缺省的构造函数
Date(int year = 1900, int month = 1, int day = 1);
// 获取某年某月的天数
int GetMonthDay(int year, int month);
void Print()
{
printf("%d %d %d
", _year, _month, _day);
}
//本质上是:
//void Print(Date* const this)
//d1.Print() 本质上是:
//d1.Print(&d1);
// 拷贝构造函数
// d2(d1)
//必须传引用,如果传值调用,会导致无限递归
Date(const Date& d);
//取地址运算符重载
const Date* operator&();
//const取地址运算符重载
const Date* operator&() const;
//函数声明
//赋值运算符重载
Date& operator=(const Date& d);
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);
//如果不设置成友元函数
// d << cout ,特别别扭,因为this指针是指向d的,传参穿的是cout流插入符号
ostream& operator<<(ostream& out);
//这两个构成重载
const Date* operator&();
const Date* operator&() const;
private:
int _year;
int _month;
int _day;
};
Date.cpp文件
#define _CRT_SECURE_NO_WARNINGS 1
#include"Date.h"
// 获取某年某月的天数
int Date::GetMonthDay(int year, int month)
{
if (year < 0 || year>9999)
{
perror("error year!");
exit(-1);
}
if (month < 0 || month > 12)
{
perror("error month!");
exit(-1);
}
static int DaysGet[13] = { 0,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;
}
else
{
return DaysGet[month];
}
}
//构造函数
Date::Date(int year = 1900, int month = 1, int day = 1)
{
if (year < 0 || year>9999)
{
perror("error year!");
exit(-1);
}
if (month < 0 || month > 12)
{
perror("error month!");
exit(-1);
}
if (day < 0 || day > GetMonthDay(year, month))
{
perror("error day!");
exit(-1);
}
_year = year;
_month = month;
_day = day;
}
//拷贝构造
Date::Date(const Date& d)
{
this->_year = d._year;
this->_month = d._month;
this->_day = d._day;
}
// 析构函数
//日期类不需要析构
//~Date();
// 赋值运算符重载
Date& Date::operator=(const Date& d)
{
//防止自己给自己赋值,可以判断一下
//注意&d的&是取地址的意思
if (this != &d)
{
this->_year = d._year;
this->_month = d._month;
this->_day = d._day;
}
return *this;
}
//取地址运算符重载
const Date* Date::operator&()
{
return this;
}
//const取地址运算符重载
const Date* Date::operator&() const
{
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 += 1;
if (_month > 12)
{
_year += 1;
_month = 1;
}
}
return *this;
}
// 日期+天数
Date Date::operator+(int day)
{
//可以复用 +=
Date tmp(*this);
tmp.operator+=(day);
return tmp;
}
// 日期-=天数
Date& Date::operator-=(int day)
{
if (day < 0)
{
return *this += -day;
}
_day -= day;
while (_day < 1)
{
--_month;
if (_month < 1)
{
--_year;
_month = 12;
}
_day += GetMonthDay(_year, _month);
}
return *this;
}
// 日期-天数
Date Date::operator-(int day)
{
//可以复用 -=
Date tmp(*this);
tmp.operator-=(day);
return tmp;
}
//日期-日期
int Date::operator-(const Date& d)
{
//小-大返回负数,大-小返回正数
Date max = *this;
Date min = d;
int flag = 1;
//如果假设错误,交换一下
if (max < min)
{
max = d;
min = *this;
flag = -1;
}
int days = 0;//计算天数差
while (min != max) //!=更高效, <还要比年月日
{
++days;
++min;
}
return flag * days;
}
// >运算符重载
//实现一个大于和一个等于就可以完成其他所有的操作了
bool Date::operator>(const Date& d)
{
if (_year > d._year)
{
return true;
}
if (_year == d._year && (_month > d._month))
{
return true;
}
if (_year == d._year && (_month > d._month) && _day > d._day)
{
return true;
}
return false;
}
// ==运算符重载
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);
}
// 前置++
Date& Date::operator++()
{
//this->operator+=(1);
*this += 1;
return *this;
}
// 后置++
Date Date::operator++(int)
{
Date tmp(*this);
//this->operator+=(1);
tmp += 1;
return tmp;
}
// 前置--
Date& Date::operator--()
{
//this->operator-=(1);
*this -= 1;
return *this;
}
// 后置--
Date Date::operator--(int)
{
Date tmp(*this);
//tmp.operator-=(1);
tmp -= 1;
return tmp;
}
//友元函数
// Date d 最好加const
//ostream& operator<<(ostream& out, const Date d)
//{
// out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
// return out;
//}
// d << cout
//不能加const,加了报错,不能限制流的改变
ostream& Date::operator<<(ostream& out)
{
out << _year << "年" << _month << "月" << _day << "日" << endl;
return out;
}
?总结
本文实现了一个具体的日期类及其功能。