日期类的实现

目录

  • 日期类的实现
  • 比较功能的实现
    • 日期类的构造函数
      • Date::Date(int year,int month,int day)代码
    • 判断日期大小
      • 判断日期类d1是否小于d2
        • bool Date::operator<(const Date& d)代码
      • 判断日期类d1是否小于等于d2
        • bool Date::operator<=(const Date& d) 代码
      • 判断日期类d1是否大于d2
        • bool Date::operator>(const Date& d)代码
      • 判断日期类d1是否大于等于d2
        • bool Date::operator>=(const Date& d)代码
      • 判断日期类d1是否等于d2
        • bool Date::operator==(const Date& d)代码
        • bool Date::operator!=(const Date& d)代码
  • 比较函数实现总结
  • 日期类的加减乘除
    • 日期加天数的实现
        • int GetMonthDay(int year, int month)代码
      • 日期加写法一
        • 代码Date Date:: operator+(int day )
      • 日期加写法二
        • Date& Date:: operator+=(int day)代码
        • Date& Date:: operator+(int day)代码
        • 拓展
      • 补充
    • 日期减天数的实现
      • Date& Date::operator-=(int day)代码
      • Date Date::operator-(int day)代码
    • 日期减日期的实现

感谢各位大佬对我的支持,如果我的文章对你有用,欢迎点击以下链接
🐒🐒🐒 个人主页
🥸🥸🥸 C语言
🐿️🐿️🐿️ C语言例题
🐣🐣🐣 python
🐓🐓🐓 数据结构C语言
🐔🐔🐔 C++
🐿️🐿️🐿️ 文章链接目录

日期类的实现

//Date.h文件
class Date
{
public:Date(int year = 1900, int month = 1, int day = 1);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);Date  operator+(int day);Date  operator-(int day);void Print(){cout << _year << "/" << _month << "/" << _day << endl;}
private:int _year;int _month;int _day;
};

比较功能的实现

日期类的构造函数

Date::Date(int year,int month,int day)代码

//Date.cpp文件
Date::Date(int year,int month,int day)
{_year = year;_month = month;_day = day;
}

判断日期大小

判断日期类d1是否小于d2

bool Date::operator<(const Date& d)代码
//Date.cpp文件
bool Date::operator<(const Date& d)
{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) 代码
//Date.cpp文件
bool Date::operator<=(const Date& d)
{return *this < d || *this == d;
}

判断日期类d1是否大于d2

bool Date::operator>(const Date& d)代码
//Date.cpp文件
bool Date::operator>(const Date& d)
{return !(*this <= d);
}

判断日期类d1是否大于等于d2

bool Date::operator>=(const Date& d)代码
//Date.cpp文件
bool Date::operator>=(const Date& d)
{return !(*this < d);
}

判断日期类d1是否等于d2

bool Date::operator==(const Date& d)代码
//Date.cpp文件
bool Date::operator==(const Date& d)
{return _year == d._year&& _month == d._month&& _day == d._day;
}
bool Date::operator!=(const Date& d)代码
bool Date::operator!=(const Date& d)
{return !(*this == d);
}

比较函数实现总结

通过上面的代码我们可以看出函数是可以互用的,并且代码中灵活的利用this指针与引用d进行比较,得出最后的是否为真

日期类的加减乘除

两个日期类相加在现实中并没有意义,而一个日期类加一个天数,得到的新的日期类,以及两个日期类相减,在现实中有很大意义
在实现这些功能之前我们需要理解运算符重载中的一句话:重载操作符必须有一个类类型参数,这里的必须表示最少有一个类就可以,说明重载操作符是可以支持其他类型的参数的

日期加天数的实现

对于日期类的加减天数需要注意的问题就是进位
我们知道每个月的天数不是一样的,比如1月和4月,1月是31天,而4月是30天,在加减天数的时候需要将所在月份的天数考虑进去,特别要注意2月,因为闰年的2月和平年的2月天数是不一样的,所以还需要判断是闰年还是平年

我们先看看进位的思想,比如2024/5/11加5天是不用进位的,因为16小于31

在这里插入图片描述
而当我们继续加上20天后,原来的16就变成了36,36大于31,所以需要进位
在这里插入图片描述
进位后需要将原来的36减去31,同时让5变到6
在这里插入图片描述
有了这个思路,接下来就是具体实现了
再实现功能前我们需要在Date的头文件中创建一个GetMonthDay函数,目的是调用函数的时候可以获得当月的天数
因为除了2月其他的月份天数都是固定的,所以我们可以用一个数组monthDays去存储每个月份的天数
这里有一个细节,我们让monthDays[0]=0,因为月份是从1开始的,所以在访问数组的时候,我们只需要从下标为1开始访问就可以了,下标为0就当他不存在

int GetMonthDay(int year, int month)
{assert(month > 0 && month < 13);int monthDays[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)&&month==2){return 29;}return monthDays[month];
}

这段代码是可以进一步优化的
第一个优化是GetMonthDay是一个函数,并且在这个日期类中使用是非常频繁的
我们都知道函数的调用会开辟栈帧,函数频繁调用,每次都要创建数组 monthDays,而开辟的这些空间里装的都是同一个数据,这不是我们想看到的结果
所以用static修饰 monthDays(因为静态区只会创建出一个数组)

第二个优化是if语句(虽然这个优化可能没什么作用),将month==2和前面的条件判断交换位置

if ( month == 2&&(year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))

最后GetMonthDay代码如下,但是要注意GetMonthDay要放在公有的区域里,如果放在私有就访问不了了

int GetMonthDay(int year, int month)代码
//Date.h
int GetMonthDay(int year, int month)
{assert(month > 0 && month < 13);static int monthDays[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;}return monthDays[month];
}

接下来就是实现日期相加的功能
根据上面的逻辑实现的代码如下

日期加写法一

Date& Date:: operator+(int day )
{_day += day;while (_day > GetMonthDay(_year, _month))//判断是否大于当前月的天数{_day -= GetMonthDay(_year, _month);//让日期减去当月的天数++_month;if (_month == 13)//month=13就代表应该要让年进一位了{++ _year;_month = 1;//让month回到1}}return *this;//返回修改后的类
}

我们测试一下
在这里插入图片描述

在这里插入图片描述

测试后看起来的确是没有问题的,但是我们再来看看下面这个情况
当我们调用d1.Print()后发现d1被修改了
在这里插入图片描述

问题其实是在d2=d1+21上,d1+21会调用operator+(int day )
而这个函数中的_day += day,++_month,++ _year,都是改变的d1,然后将修改后的d1作为返回值(注意这里还是会被临时变量拷贝),d2会拷贝临时变量,最后导致d1和d2都被修改
所以我们上面实现的函数其实是operator+=(int day ),而不是operator+(int day )

那如何去实现operator+(int day )呢?
我们可以通过拷贝构造,将d1拷贝一份给tmp,然后后面的过程都不变,后面这样还是会改变tmp,但是tmp只是一个局部变量,出来函数作用域后就不存在了,所以没有影响,但是要注意tmp是局部变量,所以返回类型不能用引用

代码Date Date:: operator+(int day )
//Date.cpp文件
Date Date:: operator+(int day )
{Date tmp(*this);tmp._day += day;//tmp拷贝后,后面的_day...都是tmp的成员变量while (tmp._day > GetMonthDay(_year, _month))//大于当前月的天数{tmp._day -= GetMonthDay(tmp._year, tmp._month);++tmp._month;if (tmp._month == 13){++tmp._year;tmp._month = 1;}}return tmp;//因为tmo拷贝的是*this,所以只需要返回tmp,而不是*tmp
}

在这里插入图片描述

日期加写法二

写法一中在实现operator+(int day )的时候我们意外实现出了operator+=(int day ),从代码的逻辑上看这两个函数几乎都是相同的,那可不可以互用呢?

Date& Date:: operator+=(int day)代码
Date& Date:: operator+=(int 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)代码
Date Date:: operator+(int day)
{Date tmp=*this;//或者Date tmp(*this);tmp+= day;return tmp;
}

我们只需要对 operator+(int day)稍微改一下就可以了
在这里插入图片描述
对比一下两个写法,我们可以发现红色方框和绿色方框的实现是几乎一样的,唯独深蓝色方框中的tmp+=day是额外加上去的

这是因为tmp+=day可以调用 operator+=(int day),也就是operator+(int day)函数实现的内部互用了operator+=(int day),这个实现的逻辑和之前判断日期大小方式相同
在这里插入图片描述

拓展

既然实现了operator+=(int day)可以让operator+(int day)互用,那实现了operator+(int day)是否也可以让operator+=(int day)互用呢

Date Date:: operator+(int day)
{Date tmp(*this);tmp._day += day;while (tmp._day > GetMonthDay(_year, _month))//大于当前月的天数{tmp._day -= GetMonthDay(tmp._year, tmp._month);++tmp._month;if (tmp._month == 13){++tmp._year;tmp._month = 1;}}return tmp;
}
Date&Date::operator+=(int day)
{*this=*this+day;return *this;
}

因为operator+=(int day)要让this也要改变,而this+day虽然互用了operator+(int day),但是operator+(int day)是通过拷贝构造出一个tmp去修改的,函数内部并没有改变this指针,所以要解决这个问题我们只需要写成*this=*this+day就可以解决了
operator+(int day)互用operator+=(int day)和operator+=(int day)互用operator+(int day)都实现完后,他们的实现方法是否都一样呢?
可以看到一个是引用返回,另一个不是,这其实是因为我自己忘记加上了🤣🤣,并不是不可以加引用符号,因为返回的都是this指针,并不是临时变量tmp,对于返回的是tmp的函数不可以加引用符号,因为出了作用域会被销毁
在这里插入图片描述

那加上后请问哪个的实现方法更好呢?
在对比的时候我们要+对比+,+=对比+=,
在这里插入图片描述
先对比+的实现,+的实现对比下来开辟的空间都是一样的
在这里插入图片描述
再对比+=的实现
在这里插入图片描述
所以最终右边的实现方法更好一点,因为少开了一点空间

补充

Date tmp(*this)可不可以改成Date tmp=*this,改之后是拷贝构造还是赋值呢
我们需要再一次理解一下拷贝构造和赋值
拷贝构造:同类型的一个存在对象进行初始化要创建的对象
拷贝赋值:已经存在的对象,一个拷贝赋值给另一个

Date tmp=this中的tmp并不是一个已经存在的对象,而是用一个已经存在的对象this去初始话另一个正在构造的对象tmp,所以这虽然是用的赋值符号=,但是从定义来看这是一个拷贝构造
同样的Date tmp(*this)也是拷贝构造

日期减天数的实现

根据上面代码实现的对比,日期类减天数的实现我们应该优先实现operator-=(int day),再实现operator-(int day)
这个函数的实现思想主要是借位
在这里插入图片描述
当_day<=0的时候就需要让_month减1,然后给_day加上_month减去1后对应的天数,当_month==0的时候就需要让_year减1,让_month=12

Date& Date::operator-=(int day)代码

//Date.cpp文件
Date& Date::operator-=(int day)
{_day -= day;while (_day <= 0){--_month;if (_month == 0){--_year;_month = 12;}_day += GetMonthDay(_year,_month);}return *this;
}

这段代码中的–_month,和条件判断中的(_month == 0)有效的解决了当减去非常多的天数时,也就是month变成<0的情况
operator-(int day)的实现就和operator+(int day)一样了

Date Date::operator-(int day)代码

//Date.cpp文件
Date Date::operator-(int day)
{Date tmp = *this;tmp -= day;return tmp;
}

在这里插入图片描述
我们再减一些比较大的值,这些结果可以在网上的日期计算器里去验证
在这里插入图片描述
再来验证一下+的实现
在这里插入图片描述

日期减日期的实现

日期减日期非常麻烦,我们都知道闰年和平年是相差1天的,在相减时年回不断的变化,并且在减的过程当中可能会出现多个闰年和平年,这样减会非常麻烦

那有什么办法可以简化的吗?
看看下面这个代码

int Date::operator-(const Date& d)
{int flag = 1;Date max = *this;Date min = d;if (*this < d){int flag = -1;max = d;min = *this;}int n = 0;while (min != max){++min;++n;}return n * flag;
}

我们在函数中创建了两个对象max和min,max=*this,min=d,以及一个整形flag=1(这个flag用处很大)

但是对于this和d我们并不知道谁大谁小,所以在执行函数的过程当中需要加一个条件判断,如果this<d就让两个对象交换,并且让flag=-1

之后定义一个n,用于计算出min和max之间相差多少天

最后返回的是nflag,因为不确定this和d的大小,所以需要借助flag确定最后减出的值是正还是负

这个函数是互用了前面的前置++(前置++在(运算符重载(下))中实现的),以及Date Date::operator-(int day),所以要想实现这个函数,前面的函数也必须得实现出来

此外这个函数中有一个细节就是while的条件判断中并没有用min<max来作为条件判断,因为小于的比较逻辑更复杂,需要将年月日都进行比较,所以选择用min!=max
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/339810.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LabVIEW版本控制

LabVIEW作为一种流行的图形化编程环境&#xff0c;在软件开发中广泛应用。有效地管理版本控制对于确保软件的可靠性和可维护性至关重要。LabVIEW提供了多种方式来管理VI和应用程序的修订历史&#xff0c;以满足不同规模和复杂度的项目需求。 LabVIEW中的VI修订历史 LabVIEW内置…

数学建模 —— 灰色系统(4)

目录 什么是灰色系统&#xff1f; 一、灰色关联分析 1.1 灰色关联分析模型 1.2 灰色关联因素和关联算子集 1.2.1 灰色关联因素 1.2.2 关联算子集 1.3 灰色关联公理与灰色关联度 1.3.1 灰色关联度 1.3.2 灰色关联度计算步骤 1.4 广义关联度 1.4.1 灰色绝对关联…

宏集JMobile Studio—实现HMI界面高自由度设计

一、简介 物联网HMI的组态软件是数据可视化的重要工具&#xff0c;工程师可以通过图形化界面来配置、监控和管理现场采集的数据。目前&#xff0c;市面上大多数的组态软件里的可视化控件库都由设计师预先部署&#xff0c;用户只能调用而不能完全自定义控件&#xff0c;导致可视…

强化学习 (三) 动态规划

文章目录 迭代法网友认为的迭代策略评估与价值迭代的区别 迭代策略评估的进一步解释附录 传统dp作用有限&#xff1a; 需要完备的环境模型计算的复杂度极高 其它方法都是对dp的近似&#xff0c;近似的出发点是解决上面两个问题。 有一种说法是&#xff0c;强化学习其实就是拟…

回溯算法常见思路

回溯问题 回溯法&#xff0c;一般可以解决如下几种问题&#xff1a; 组合问题&#xff1a;N个数里面按一定规则找出k个数的集合切割问题&#xff1a;一个字符串按一定规则有几种切割方式子集问题&#xff1a;一个N个数的集合里有多少符合条件的子集排列问题&#xff1a;N个数…

使用 Scapy 库编写 ICMP 重定向攻击脚本

一、介绍 ICMP重定向攻击&#xff08;ICMP Redirect Attack&#xff09;是一种网络攻击&#xff0c;攻击者通过发送伪造的ICMP重定向消息&#xff0c;诱使目标主机更新其路由表&#xff0c;以便将数据包发送到攻击者控制的路由器或其他不可信任的设备上。该攻击利用了ICMP协议…

昆仑万维官宣开源2000亿稀疏大模型Skywork-MoE

6月3日&#xff0c;昆仑万维宣布开源2千亿稀疏大模型Skywork-MoE&#xff0c;性能强劲&#xff0c;同时推理成本更低。 据「TMT星球」了解&#xff0c;Skywork-MoE基于之前昆仑万维开源的Skywork-13B模型中间checkpoint扩展而来&#xff0c;是首个完整将MoE Upcycling技术应用…

上位机图像处理和嵌入式模块部署(f103 mcu获取唯一id)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 对于stm32f103系列mcu来说&#xff0c;一般每一颗原厂的mcu&#xff0c;都会对应一个唯一的id。那这个id可以用来做什么用呢&#xff1f;个人认为&…

windows配置dns访问git , 加快访问速度保姆级教程

设置 DNS 访问 Git 需要修改电脑的 DNS 配置。下面是具体的操作流程&#xff1a; 第一步&#xff1a;打开命令提示符或终端窗口 在 Windows 系统中&#xff0c;可以按下 Win R 组合键&#xff0c;然后输入 “cmd”&#xff0c;按下 Enter 键打开命令提示符窗口。在 macOS 或 …

学习C++应该做点什么项目

C作为一门底层可操作性很强的语言&#xff0c;广泛应用于游戏开发、工业和追求性能、速度的应用。 比如腾讯&#xff0c;无论游戏&#xff0c;还是微信&#xff0c;整个鹅厂后台几乎都是 C 开发&#xff0c;对 C 开发者的需求非常大。 但问题是C入门和精通都比较困难&#xf…

62. UE5 RPG 近战攻击获取敌人并造成伤害

在上一篇&#xff0c;我们实现了通过AI行为树控制战士敌人靠近攻击目标触发近战攻击技能&#xff0c;并在蒙太奇动画中触发事件激活攻击的那一刻的伤害判断&#xff0c;在攻击时&#xff0c;我们绘制了一个测试球体&#xff0c;用于伤害范围。 在之前实现的火球术中&#xff0c…

CAC2.0全生命周期防护,助力企业构建安全闭环

5月29日&#xff0c;CACTER邮件安全团队凭借多年的邮件安全防护经验&#xff0c;在“防御邮件威胁-企业如何筑起最后防线”直播分享会上展示了构建安全闭环的重要性&#xff0c;并深入介绍了全新CAC 2.0中的“威胁邮件提示”功能。 下滑查看更多直播精彩内容 构建安全闭环的必要…

【第3章】SpringBoot实战篇之登录接口(含JWT和拦截器)

文章目录 前言一、JWT1. 什么是JWT2. 使用场景3. 结构3.1 Header3.2 Payload3.3 Signature 4. 使用 二、案例1.引入库2.JwtUtils3. UserController14. ArticleController 三、拦截器1. 定义拦截器2. 注册拦截器 四、测试1. 登录2. 无token3. 有token4. 全局配置 总结 前言 前面…

JAVAEE之网络初识_协议、TCP/IP网络模型、封装、分用

前言 在这一节我们简单介绍一下网络的发展 一、通信网络基础 网络互连的目的是进行网络通信&#xff0c;也即是网络数据传输&#xff0c;更具体一点&#xff0c;是网络主机中的不同进程间&#xff0c;基于网络传输数据。那么&#xff0c;在组建的网络中&#xff0c;如何判断到…

遥感之特征选择-禁忌搜索算法

各类智能优化算法其主要区别在于算法的运行规则不同&#xff0c;比如常用的遗传算法&#xff0c;其规则就是变异&#xff0c;交叉和选择等&#xff0c;各种不同的变体大多是在框架内的实现细节不同&#xff0c;而本文中的禁忌算法也是如此&#xff0c;其算法框架如下进行介绍。…

【IDEA】-使用IDEA查看类之间的依赖关系

1、父子类的继承、实现关系 1.1、使用CTRL Alt U 选择 java class 依据光标实际指向的类位置 用实心箭头表示泛化关系 是一种继承的关系&#xff0c;指向父类 可以提前设置需要显示的类的属性、方法等信息 快捷键 Ctrl Alt S &#xff0c;然后搜索 Diagrams 1.2、使用…

鸿蒙开发接口资源调度:【@ohos.backgroundTaskManager (后台任务管理)】

后台任务管理 本模块提供后台任务管理能力。 当应用或业务模块处于后台&#xff08;无可见界面&#xff09;时&#xff0c;如果有需要继续执行或者后续执行的业务&#xff0c;可基于业务类型&#xff0c;申请短时任务延迟挂起&#xff08;Suspend&#xff09;或者长时任务避免…

C语言学习笔记之结构体(一)

目录 什么是结构体&#xff1f; 结构体的声明 结构体变量的定义和初始化 结构体成员的访问 结构体传参 什么是结构体&#xff1f; 在现实生活中的很多事物无法用单一类型的变量就能描述清楚&#xff0c;如&#xff1a;描述一个学生&#xff0c;需要姓名&#xff0c;年龄&a…

Lua的几个特殊用法

&#xff1a;/.的区别 详细可以参考https://zhuanlan.zhihu.com/p/651619116。最重要的不同就是传递默认参数self。 通过.调用函数&#xff0c;传递self实例 通过 &#xff1a; 调用函数&#xff0c;传递self (不需要显示的传递self参数&#xff0c;默认就会传递&#xff0c;但…

旧衣回收小程序带来的收益优势,小程序有哪些功能?

随着互联网的快速发展&#xff0c;大众对旧衣回收市场也越来越了解&#xff0c;对于闲置的旧衣物也有了适合的处理方式。旧衣回收也符合了当下资源回收利用&#xff0c;因此&#xff0c;旧衣回收市场获得了爆发式增长&#xff0c;市场规模不断扩大。同时市场中还吸引了越来越多…