如何用C++写一个日期计算器

目录

前言

代码的布局

设计数据

方法声明

方法的实现

获取某年某月的天数

*全缺省的构造函数

* 拷贝构造函数

*赋值运算符重载

*析构函数

日期+=天数

日期+天数

日期-天数

日期-=天数

前置++

后置++

后置--

前置--

实现比较大小运算符重载思路

>运算符重载

==运算符重载

*>  = 运算符复用实现其他比较运算符重载

>=运算符重载

<运算符重载

<=运算符重载

!=运算符重载

日期-日期

代码错误和bug分享


前言

   写一个日期计算器是一个很有现实意义的。比如,我想弄一个倒计时或者你女朋友问你和她认识多少天了,这就需要让两个日期相减。又或者和别人约定几年以后怎么怎么样,这就得知道那一天的日期。

   写一个日期计算器对学习的意义也很大。初学C++,接触了类和对象的概念,又认识了默认成员函数,然后又学习了运算符的重载。而日期计算器就很好的涵盖了这些知识。能很好的帮助我们复习学过的知识。

对上述知识比较模糊的话,可以参考小编的另外几篇文章

详解构造函数:  http://t.csdnimg.cn/jbRlx

详解拷贝构造:   http://t.csdnimg.cn/Jm2k2

详解运算符重载,赋值运算符重载,++运算符重载:   http://t.csdnimg.cn/H7MZU

   小编还会分享一下写代码时遇到的bug与错误。本篇内容如有不足之处,还请指正,小编会虚心接受并及时改进质量。



代码的布局

建两个 .cpp文件:Date.cpp      Test.cpp

建一个   .h文件  :Date.h

作用:Date.h声明一个类 ,      Date.cpp类的方法的具体实现,      Test.cpp测试方法的逻辑



设计数据

年:_year     月:_month     日:_day

变量名前面加“  _ ”符号,是为了和普通的数据或一些参数做区分。增加代码可读性。



方法声明

	// 获取某年某月的天数int GetMonthDay(int year, int month);// 全缺省的构造函数Date(int year = 2024, int month = 4, int day = 18);// 拷贝构造函数Date(const Date& d);// 赋值运算符重载Date& operator=(const Date& d);// 析构函数~Date();// 日期+=天数Date& operator+=(int day);  //其结果为日期// 日期+天数Date operator+(int day);// 日期-天数Date operator-(int day);// 日期-=天数Date& operator-=(int day);  // 前置++Date& operator++();  //天数加1// 后置++Date operator++(int);// 后置--Date operator--(int);  //天数减1// 前置--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);  

声明的方法要有其意义,比如日期和天数相乘就没有意义,也没必要声明。



方法的实现

获取某年某月的天数

int GetMonthDay(int year, int month);

闰年

一年有365天,但地球公转的周期比一年多了大约5.82个小时。所以每过4年,二月的28天就要变成29天,即4年一润。但每四年都要润一次的话,我们计算的天数要比地球公转的天数多了大概0.75天,所以二月的28天保持不变,即百年不润。100年不润是为了补足4年一润的精度,而400一润是为了补足100年不润的精度。只有这样,日期才不会与四季脱离。

总结就是:四年一润,百年不润,四百年又一润。

翻译成计算机语言就是

 year % 4 == 0 && year % 100 != 0 || year % 400 == 0

有了闰年的概念,那么获取某年某月的天数的代码就可以实现了

// 获取某年某月的天数
int Date::GetMonthDay(int year, int month)    
{assert(month > 0 && month < 13);static int a[13] = { -1,31, 28, 31, 30, 31, 30, 31, 31, 30, 31,30,31 };if (2 == month && year % 4 == 0 && year % 100 != 0 || year % 400 == 0){return a[month] + 1;}else{return a[month];}
}

下图是代码控制的细节

下面加*的函数不做重点

*全缺省的构造函数

Date(int year = 2024, int month = 4, int day = 18);
Date::Date(int year, int month, int day)
{_year = year;_month = month;_day = day;
}

注意:全缺省构造函数在定义的时候不需要给缺省值。

* 拷贝构造函数

Date(const Date& d);
Date::Date(const Date& d)
{_year = d._year;_month = d._month;_day = d._day;}

*赋值运算符重载

Date& operator=(const Date& d);
Date& Date::operator=(const Date& d) 
{if (this != &d)  {_year = d._year; _month = d._month; _day = d._day;return *this;}return *this;
}

*析构函数

因为没有涉及到资源管理可以不写,编译器会自动生成默认的析构函数。



日期+=天数

Date& operator+=(int day);

思路:可以把要加的天数直接加到日期的天数上,如果日期的天数没有超过该月的最大天数,直接返回日期。如果超过了,就写个循环往前进位,直到日期的天数小于该月的最大天数,然后再返回日期。

逻辑示意图

代码

Date& Date::operator+=(int day) 
{_day += day; //把天数加到日期的天数上while (_day > GetMonthDay(_year, _month)) //如果日期的天数大于该月最大天数就进位                                     {_day -= GetMonthDay(_year, _month); //要想进位,得把该月的最大天数减掉++_month; //进位if (_month > 12) //如果月不合法就调整月{++_year;_month = 1;}}return *this;  //返回日期,因为出了作用域不会销毁,可以引用返回
}

*this是返回声明在头文件中的日期类,该类出了该函数的作用域不会销毁,所以传引用返回,提高效率。



日期+天数

Date operator+(int day);

实现日期+天数的时候不用把类似于日期+=天数的逻辑再写一遍,可以直接复用。

Date Date::operator+(int day)
{Date tmp = *this;  //在实例化对象的时候,调用拷贝构造函数,将日期类的数据拷贝给临时对象tmp += day;  //直接复用+=的逻辑return tmp;
}

日期加天数不能改变日期的值,所以要创建临时对象。临时对象出了作用域就销毁了,所以不能传引用返回。



日期-天数

	Date operator-(int day);

在实现日期+=天数和日期+天数的时候,先实现了+=的逻辑,在实现+的逻辑的时候复用+=的逻辑。现在反过来,先实现-的逻辑,在实现-=的逻辑的时候复用-的逻辑。

思路:把天数直接和日期中的天数相减。若不为负数,直接返回。若为负数,则需要写个循环不断向前借位,如果把月借成负数就向年借,然后调整月,再调整日,直到日大于零为止。因为日期-天数不改变日期,所以要创建临时的对象。

代码

Date Date::operator-(int day)
{Date d = (*this); //创建临时对象,把日期类的数据拷贝给临时对象d._day -= day;  //让日期的天数直接和天数相减while (d._day <= 0)  //日期的天数小于零就调整{--d._month;  //该月已经是负的,应该往下个月借天数if (d._month <= 0)  //月不合法就调整月{--d._year;d._month = 12;}d._day += GetMonthDay(d._year, d._month); //把该月的所有天数都借给日期的天数}return d; }

逻辑示意图

因为临时对象出了作用域要销毁,所以不能传引用返回。

日期-=天数

Date& operator-=(int day);  

直接复用-的逻辑,代码如下

Date& Date::operator-=(int day)
{return *this = *this - day;
}


前置++

Date& Date::operator++() 

实现前置++就不需要复杂的逻辑了,只需要控制年月日的进位即可。代码如下

Date& Date::operator++()  //前置++需要先++在使用,所以不需要创建临时对象,返回值可以是引用
{++_day;   //天数加一if (_day > GetMonthDay(_year, _month))  //如果天数不符合该月最大天数,则需要调整{++_month;  //让月加一if (_month > 12)  //月不合法就调整月{++_year;_month = 1;}_day = 1;  让天数置一}return *this;  //返回该类
}

后置++

Date Date::operator++(int)

可直接复用前置++,后置++需要先使用再++,所以需要创建临时对象,代码如下

Date Date::operator++(int)
{Date d = *this;++(*this);return d;
}

后置--

Date Date::operator--(int)

与++的实现不同,--的话先实现后置再实现前置。代码如下

Date Date::operator--(int)
{Date d = *this;  // 创建临时对象,保存日期类中的值--_day;  //日期类中的天数减一if (_day <= 0) //这里可以不用写小于,因为一天一天的减是不可能跨过零来到负数的{--_month;  //如果天数等于零了,就需要借上个月的天数,月要减一if (_month <= 0) //月不合法就调整月{--_year;_month = 12;}_day = GetMonthDay(_year, _month);  //把天数置成该月最大天数}return d;  //返回保存好的数据,这样就实现了后置--的效果
}

前置--

Date& Date::operator--()

直接复用后置--,代码如下

Date& Date::operator--()
{(*this)--;return *this;
}


实现比较大小运算符重载思路

小编先理一下思路,方便大家理解。

要实现比较大小的运算符有 >==>= < , <= , !=只需要实现> 和  ==就可以复用并实现后四个运算符。如下图

>运算符重载

bool Date::operator>(const Date& d)

代码如下

bool Date::operator>(const Date& d)
{if (_year > d._year)  //年大就大{return true;}if (_year == d._year)//年相等比月{if (_month > d._month) //月大就大{return true;}if (_month == d._month) //月相等比天{if (_day > d._day) //天大就大{return true;}}}return false; //不然就是小的
}

==运算符重载

bool Date::operator==(const Date& d)

年月日都相等才相等,代码如下

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);
}


日期-日期

int Date::operator-(const Date& d)

参数:第一个参数为隐含的this指针,第二个参数为 const  Date&,传引用是为了提高传值效率。

返回值:日期-日期代表的是两个日期之间相差的天数,返回值类型为 int。

思路1:可以先算出两个日期相差多少年,把每一年的总天数加在一起,但要判断该年是否为闰年。

思路2:直接复用++运算符,在设一个变量,每加一天,变量就加一。

下面用思路2实现,代码如下

int Date::operator-(const Date& d)
{int counst = 0;  //定义一个变量,保存天数if ((*this) == d)  //如果两个日期相等,直接返回零{return 0;}else if ((*this) > d) {Date tmp = d; //如果this的的日期大,就给d创建临时变量tmp,然tmp小日期去追this大日期while ((*this) != tmp){++tmp;counst++;}return counst; }else{Date tmp = (*this); //同上while (tmp != d)    {++tmp;counst++;}return counst;  }}


代码错误和bug分享

小编在实现方法的时候把域作用限定符写在了返回值的前面,如下

大家不要这样写呀。

在写前置++的时候写了一个不易察觉的bug,写完测了几组数据没问题,但其他方法调用的时候却出问题了,调了好久才发现,如下代码,大家能看出来哪里出错了吗

// 前置++
Date& Date::operator++()  // bug分享   
{++_day;   if (_day > GetMonthDay(_year, _month)){++_month;if (_month > 12){++_year;_month = 1;_day = 1;}}return *this;
}

哈哈,其实正是因为逻辑太顺了,忽略了一些情况如下图

大家在测方法的时候尽量要跨过几个平年和闰年,这样方法才有可信度。


好啦,本篇的内容到此结束啦

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

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

相关文章

互联网通信原理

互联网通信原理 ISO/OSI(开放系统互连)的七层模型 注意事项 上三层是为用户提供服务的&#xff0c;下四层负责实际数据传输下四层的传输单位 传输层&#xff08;数据段&#xff09;、网络层&#xff08;数据包&#xff09;、数据链路层&#xff08;数据帧&#xff09;、物理层…

图文教程 | 2024年最新Typora激活使用教程合集

前言 汇总一下网上的三种方法。 &#x1f4e2;博客主页&#xff1a;程序源⠀-CSDN博客 &#x1f4e2;欢迎点赞&#x1f44d;收藏⭐留言&#x1f4dd;如有错误敬请指正&#xff01; 关于安装教程&#xff1a;http://t.csdnimg.cn/SCIQ8http://t.csdnimg.cn/SCIQ8自行跳转安装 一…

35. 【Android教程】视频页面:ViewPager

ViewPager 是一种可以让用户通过左右滑动来切换页面的控件&#xff0c;通过它我们可以展示超过屏幕尺寸大小的内容&#xff0c;在某种程度上它可以说是实现多页面的最佳方式&#xff0c;同时 ViewPager 还支持任意动态的添加/删除页面。比如我们可以将不同的类别的内容分别放在…

java 创建和请求sse服务

主要依赖 <!--spring-boot父工程--><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.2.2.RELEASE</version></parent><dependency><gro…

AOP基础

一、AOP概述 AOP&#xff1a;Aspect Oriented Programming&#xff08;面向切面编程、面向方面编程&#xff09;&#xff0c;其实就是面向特定方法编程。 使用场景&#xff1a;①记录操作日志&#xff1b;②权限控制&#xff1b;③事务管理等。 优势&#xff1a;①代码无侵入…

学校管网的仿写

工字形布局完成 效果 代码部分 在这里插入代码片 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport…

密码学 | Random Oracle 随机预言机

​ &#x1f951;原文&#xff1a;究竟什么才是随机预言机呢&#xff1f; - 玄星的回答 &#x1f951;答主指出&#xff1a; 英文维基明明对 随机预言机 给出了两个完全不同的理解&#xff0c;但这两个理解之间的连接词却是 “Stated differently”&#xff0c;即 “换句话说…

Unity ECS

一&#xff1a;前言 ECS与OOP不同&#xff0c;ECS是组合编程&#xff0c;而OOP的理念是继承 E表示Entity&#xff0c;每个Entity都是一个有唯一id的实体。C表示Component&#xff0c;内部只有属性&#xff0c;例如位置、速度、生命值等。S表示System&#xff0c;驱动实体的行为…

npm i 依赖下载失败

git config --global url."https://".insteadOf git://解决npm install 报错 npm ERR code 128 Permission denied_please make sure you have the correct access right-CSDN博客

怎么把相机储存卡里的照片导出来?介绍两种方法

随着摄影技术的不断发展和普及&#xff0c;相机已成为我们记录生活、捕捉美好瞬间的设备。然而&#xff0c;对于许多摄影爱好者来说&#xff0c;如何将相机储存卡里的照片安全、高效地导出到电脑或其他设备中&#xff0c;却成为了一个令人头疼的问题。本文将为您详细介绍从相机…

c++IO

前言 大家好&#xff0c;我是jiantaoyab&#xff0c;本篇文章给大家介绍c中文件操作。 先回忆一下c语言文件操作 void Test_c_bin() {//二进制写ServerInfo info { "127.0.0.1", 8080 };FILE* fout fopen("test.bin", "wb");fwrite(&in…

18 统计网站每日的访问次数

1.将竞赛的数据上传HDFS,查看数据的格式 通过浏览器访问hdfs,查看该文档前面的部分数据 每条数据的字段值之间使用逗号隔开的 &#xff0c;最终时间是第五个自动&#xff0c;获取第五个字段值的中的年月日。 2.通过Idea创建项目mr-raceData ,基础的配置 修改pom.xml,添加依赖 …

Spring Boot集成fastdfs快速入门Demo

1.什么是fastdfs FastDFS 是一个开源的高性能分布式文件系统&#xff08;DFS&#xff09;。它的主要功能包括&#xff1a;文件存储&#xff0c;文件同步和文件访问&#xff0c;以及高容量和负载平衡。主要解决了海量数据存储问题&#xff0c;特别适合以中小文件&#xff08;建议…

从零开始搭建网站(第二天)

今天把之前的htmlcssjs项目迁移过来&#xff0c;直接使用tspiniavue3vite组合&#xff0c;搭建过程可以看从零开始搭建性能完备的网站-思路过程&#xff08;1&#xff09;_自己架设一个芯参数网站-CSDN博客。之后安装一下volar扩展。迁移过来使用Vue重构时发现之前使用的左右两…

学习-官方文档编辑方法

这里写自定义目录标题 欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants 创建一个自定义列表如何创建一个…

电感与磁珠

电感是什么&#xff1f; 电感会通过产生感应电动势的方式来阻碍电流的变化&#xff0c;电流变化率越大&#xff0c;产生的感应电动势越大阻碍电流效果越明显。 [一]品质因数Q: 电感的品质因数Q值定义&#xff1a;电感的Q值也叫作品质因数&#xff0c;其为无功功率除以有功功率…

API请求报错 Required request body is missing问题解决

背景 在进行调用的时候&#xff0c;加载方法&#xff0c;提示以下错误 错误信息如下&#xff1a; {"code": 10001,"msg": "Required request body is missing: XXX","data": null,"extra": null }Required request body…

ubuntu22.04下编译ffmpeg和ffplay

Ubuntu22.04 下编译安装 ffmpeg 和 ffplay 一、下载源码包 1.1 官方下载链接&#xff1a;Download FFmpeg 可以手动下载&#xff0c;也可以命令行下载&#xff1a; wget http://www.ffmpeg.org/releases/ffmpeg-7.0.tar.xz 1.2 下载完解压 tar -xvf ffmpeg-7.0.tar.xz…

《深入浅出多模态》: 多模态经典模型:BLIP

🎉AI学习星球推荐: GoAI的学习社区 知识星球是一个致力于提供《机器学习 | 深度学习 | CV | NLP | 大模型 | 多模态 | AIGC 》各个最新AI方向综述、论文等成体系的学习资料,配有全面而有深度的专栏内容,包括不限于 前沿论文解读、资料共享、行业最新动态以、实践教程、求职…

多个路由器连接的PC端进行ping通信需要做的事

实验环境&#xff1a; 三台PC三台路由器&#xff0c;并且配置好IP 拓扑图&#xff1a; 需求描述&#xff1a; 在PC0进行与PC2的ping通信&#xff1a; 需求步骤&#xff1a; 1.1首先配置ip&#xff08;略过&#xff09; 1.2我们首先查看在只配置了IP的情况下&#xff0c;P…