【C++】如何用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测试方法的逻辑

d41d4b0d75bf4f7db8b8b103b696539f.png



设计数据

年:_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年一润。但每四年都要润一次的话,每过100年,我们计算的天数要比地球公转的天数多了大概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];
		}
}

下图是代码控制的细节

b24768dd22e64e578803dd9ab3418c4f.png

下面加*的函数不做重点

*全缺省的构造函数

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

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

逻辑示意图78bd766509ef4189a4b6133f7bdda8db.png

代码

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; 

}

逻辑示意图34f95c1cf11f4be78a9fb98f646f85e0.png

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

日期-=天数

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


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

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

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

>运算符重载

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分享

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

725a3e15179741ec869d383b4773d765.png

48bb3f6de90f476ea01e6f7c0e3c5f0d.png

大家不要这样写呀。

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

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

	}

	return *this;
}

21af940a9edc42bd9ed4e40a2eeeca6b.png

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

6a605206bdbe4dc292dcbf33deb0d0e6.png

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


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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

盲人餐厅点餐:科技之光照亮餐桌上的美食之旅

在现代社会&#xff0c;餐厅不仅是满足口腹之欲的场所&#xff0c;更是一个社交、放松的重要空间。然而&#xff0c;对于视障人士而言&#xff0c;盲人餐厅点餐这一日常行为&#xff0c;却往往伴随着诸多不便与挑战。幸运的是&#xff0c;科技的革新正为这一群体带来前所未有的…

递归神经网络(RNN)在AI去衣技术中的深度应用

在人工智能&#xff08;AI&#xff09;技术飞速发展的今天&#xff0c;图像处理和计算机视觉领域不断取得新的突破。其中&#xff0c;AI去衣技术作为一个具有挑战性的研究方向&#xff0c;引起了广大研究者和公众的关注。递归神经网络&#xff08;RNN&#xff09;作为深度学习的…

【linux】编译器使用

目录 1. gcc &#xff0c;g 编译器使用 a. 有关gcc的指令&#xff08;g同理&#xff09; 2. .o 文件和库的链接方式 a. 链接方式 b. 动态库 和 静态库 优缺点对比 c. debug 版本 和 release 版本 1. gcc &#xff0c;g 编译器使用 a. 有关gcc的指令&#xff08;g同理&…

【第19章】spring-i8n

文章目录 前言一、准备二、Java国际化1.测试类2.测试结果 三、Spring国际化1.配置文件2.测试类3.测试结果4.占位符4.1 准备4.2 测试类4.3 测试结果 总结 前言 在Spring中实现国际化Internationalization&#xff08;i18n&#xff09;其实就是完成语言的切换&#xff0c;类似于…

4.SpringCloud基础项目搭建利用RestTemplate实现远程调用

简单的微服务架构 父pom.xml <?xml version"1.0" encoding"UTF-8"?><project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apa…

短信视频提取批量工具,免COOKIE,博主视频下载抓取,爬虫

痛点&#xff1a;关于看了好多市面的软件&#xff0c;必须要先登录自己的Dy号才能 然后找到自己的COOKIE 放入软件才可以继续搜索&#xff0c;并且无法避免长时间使用 会导致无法正常显示页面的问题。 有没有一种方法 直接可以使用软件&#xff0c;不用设置的COOKIE的方法呢 …

Linux驱动开发:掌握SPI通信机制

目录标题 1、SPI简介2、SPI通信机制3、Linux内核中的SPI支持4、SPI核心API5、SPI控制器驱动6、SPI设备驱动 7、编写SPI设备驱动8、调试SPI驱动 在Linux驱动开发中&#xff0c;串行外设接口(SPI)是一种常见的高速全双工通信协议&#xff0c;用于连接处理器和各种外设。本文将深入…

会声会影滤镜怎么用 会声会影滤镜效果怎么调 会声会影视频制作教程

在进行视频剪辑时&#xff0c;合理地运用滤镜效果可以提升视频的观赏性&#xff0c;使你的作品更加出彩。这篇文章便一起来学习会声会影滤镜怎么用&#xff0c;会声会影滤镜效果怎么调。 一、会声会影滤镜怎么用 使用会声会影的滤镜效果非常简单&#xff0c;以下是具体的操作…

Rust腐蚀服务器定制地图开服

Rust腐蚀服务器定制地图开服 大家好我是艾西一个做服务器租用的网络架构师。Rust腐蚀这个游戏有很多的插件mod作者&#xff0c;在地图制作这一块也是一样&#xff0c;有些好玩的地图可能大家在map网站找到了但是不知道怎么操作设置那么今天艾西给大家说下特定定制地图怎么弄。…

Penpad获Gate Labs以及Scroll联创Sandy的投资

近期&#xff0c;Scroll上的LaunchPad &聚合收益平台Penpad迎来了重磅利好&#xff0c;该平台在前不久获得了来自于Gate Las的融资&#xff0c;在此后其又获得了Scroll联合创始人Sandy的融资&#xff0c;这也让Penpad平台成为了近期Scroll生态中最值得关注的项目之一。 事实…

《生成式AI导论》学习笔记

1.课程定位 2.什么是生成式人工智慧&#xff1f; 3. 今日的生成式人工智慧厉害在哪里&#xff1f; 4.训练不了人工智慧&#xff1f;那我训练自己 5.训练不了人工智慧&#xff1f;你可以训练你自己&#xff08;中&#xff09;——拆解问题使用工具 6.大语言模型修炼史——第一阶…

毕业设计注意事项(2024届更新中)

1.开题 根据学院发的开题报告模板完成&#xff0c;其中大纲部分可参考资料 2.毕设 根据资料中的毕设评价标准&#xff0c;对照工作量 3.论文 3.1 格式问题 非常重要&#xff0c;认真对比资料中我发的模板&#xff0c;格式有问题&#xff0c;答辩输一半&#xff01; 以word…

从零开始的软件测试学习之旅(一)理论知识篇

软件测试学习理论知识 一.软件测试分类二.什么是软件测试?软件测试选择最为经典的几个模型一.瀑布模型 :将软件生命周期进行划分二.快速原型模型三.敏捷开发 三.软件测试流程一.软件测试模型测试流程: 四.软件测试分类项目环境测试与代码 一.软件测试分类 1.按开发规模 小型…

Linux 基础命令使用创建用户

浏览网站的时候图片&#xff0c;看到一个小练习。创建用户分别位于不同的用户组。 解答下面的题目 2、建立用户使用 useradd&#xff0c;设置密码使用passwd的命令。大概不会使用命令可以借助man来解答。 先建立用户组&#xff1a; groupadd group1 # group1 不存在先建立&…

nginx学习记录-URL Rewrite

1. URL Rewrite 1.1 基本概念 有的时候我们访问后台网站时&#xff0c;会携带大量的参数&#xff0c;比如/test?id1&#xff0c;像这种ur就会携带一些参数&#xff0c;由于有些参数名会携带一些敏感信息&#xff0c;我们希望在url中隐藏传递的参数&#xff0c;比如将/test?…

轻松搭建llama3Web 交互界面 - Ollama + Open WebUI

Ubuntu下安装&#xff1a;&#xff08;官网&#xff1a;Download Ollama on Linux&#xff09; curl -fsSL https://ollama.com/install.sh | sh 就运行起来ollama了&#xff0c;不放心可以用ollama serve查看一下 ollama run llama3 就可以跑起来了&#xff0c; 那么我们肯…

线性代数基础1向量

1、向量是什么 1.1、向量的定义 在数学中&#xff0c;向量&#xff08;也称为欧几里得向量、几何向量、矢量&#xff09;&#xff0c;指具有大小和方向的量。它可以形象化地表示为带箭头的线段。箭头所指&#xff1a;代表向量的方向&#xff1b;线段长度&#xff1a;代表向量的…

【程序创建的技巧】

文章目录 导语名词源代码目标代码&#xff08;object code&#xff09;可执行代码 创建源代码文件编译和链接UNIX编译和链接Linux编译和链接Windows命令行编译器Windows编译器 总结 导语 假设您编写了一个 C 程序。 如何让它运行起来呢&#xff1f; 具体的步骤取决于计算机环境…

CountDownLatch源码分析

1.创建 CountDownLatch latch new CountDownLatch(5); 2.latch.countDown(); 将count执行减一操作&#xff0c;当count为0时&#xff0c;等待中的线程会被唤醒 SIGNAL (值为-1)&#xff1a; 表示后继节点需要被唤醒。当一个节点释放锁的时候&#xff0c;会唤醒它的后继节点…

javaScript中的this 指向

this 指向总结 this 关键字是一个非常重要的语法点。 this 可以用在构造函数之中&#xff0c;表示实例对象。除此之外&#xff0c;this 还可以用在别的场合。但不管是什么场合&#xff0c;this 都有一个共同点&#xff1a;它总是返回一个对象。 关于 this 的指向&#xff0c…