2024年最全C++模拟实现-----日期计算器(超详细解析,小白一看就会!(1),农民工看完都学会了

img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

🍋Date.cpp

🥝Test.cpp

🍍代码运行的界面

五、共勉


一、前言

在之前的博客学习中,我们已经详细的讲解了C++中的引用、缺省函数、this指针、构造函数、析构函数、拷贝构造函数、运算符重载等非常重要的知识,但是对于这些知识如何如何的使用还没有进行讲解,所以本次博客将以日期计算器为例,将以上知识融合起来讲解,帮助大家更好的理解。

二、日期类计算器

在我们的日常生活中,我们可能需要计算几天后的日期,或计算日期差等,现如今计算日期的方式有很多,简单粗暴的直接查看日历,快捷点的直接使用日期计算器来求得,先给一个网络上的日期计算器截图:

        现在,就让我们用代码来实现其工作原理吧。

三、日期计算器的实现

🍎日期计算器各个接口的实现

这里先建立三个文件:
1️⃣ :Date.h文件,用于类中—函数声明
2️⃣ :Date.cpp文件,用于类中—函数的定义
3️⃣ :Test.cpp文件,用于测试函数
建立三个文件的目的: 将日期计算器作为一个项目来进行书写,方便我们的学习与观察。

🍐日期计算器的需求

这里我们需要考虑,我们实现的这个日期计算器,需要实现怎样的需求呢

  1. 需要打印当前的日期(请检查日期是否合理)
  2. 实现日期之间的大小比较
  3. 实现日期的加减天数计算
  4. 实现日期与日期之间的减法运算

🍉打印当前日期(并检查日期是否合理)

实现日期类首先就得检查日期的合法性,这其中就包括大小月,闰年的2月有29天,一年只有12个月等等细节都要考虑到。

💦检查日期是否合理
//判断是否为闰年
bool Date::isLeaveYear(int year)
{
	// (四年一润,百年不润) 或者 四百年一润
	return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
}

// 获取每个月的天数
int Date::GetMonthDay(int year, int month)
{
	int monthday[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
	if (month == 2 && isLeaveYear(year))
	{
		return 29;
	}
	else
	{
		return monthday[month];
	}
}
💦日期类构造函数(用于日期的初始化)
//判断是否为闰年
bool Date::isLeaveYear(int year)
{
	// (四年一润,百年不润) 或者 四百年一润
	return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
}

// 获取每个月的天数
int Date::GetMonthDay(int year, int month)
{
	int monthday[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
	if (month == 2 && isLeaveYear(year))
	{
		return 29;
	}
	else
	{
		return monthday[month];
	}
}
效果展示

1️⃣:错误的日期演示   ------  -1 / -1 / -1 (年/月/日)

// 测试初始化
void Test()
{
	Date d1(-1,-1,-1);
	d1.Printf();
}
int main()
{
	Test();
	return 0;
}

1️⃣:正确的日期演示  --------  2023 / 10 / 28 (年/月/日)

// 测试初始化
void Test()
{
	cout << "请输入今日的日期" << endl;
	Date d1(2023,10,28);
	d1.Printf();
}
int main()
{
	Test();
	return 0;
}

🍓日期之间的大小比较

💦<  运算符重载
  • 思路:

< 运算符重载在我上一篇博文已经详细讲解过,主要是先把大于的情况全部统计出来,就比如我要比较实例化对象d1是否小于实例化对象d2,只需考虑如下三种满足的情况:

  • d1的年小于d2的年
  • d1与d2年相等,d1的月小于d2的月
  • d1与d2年相等月相等,d1的天小于d2的天

这三种全是小于的情况,返回true,其余返回false

  • 代码如下:
// "<" 运算符重载
bool Date::operator<(const Date& d)
{
	if (_year < d._year ||
		_year == d._year && _month < d._month ||
		_year == d._year && _month == d._month && _day < d._day)
	{
		return true;
	}
	else
	{
		return false;
	}
}
💦== 运算符重载
  • 思路:

== 运算符重载其实非常简单,只需要判断d1和d2的年、月、天是否对应相等即可:

  • 代码如下:
// "==" 运算符重载
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);
}
效果展示

此时我们拿  "<"运算符举例子

// 测试"<"运算符
void Test1()
{
	Date d1(2023,10,28);
	cout << "d1的日期为:";
	d1.Printf();
	cout << "d2的日期为:";
	Date d2(2023, 10, 27);
	d2.Printf();
	cout << endl;
	if (d2 < d1)
	{
		cout << "d1的日期 < d2的日期" << endl;
	}
	else
	{
		cout << "d2的日期 > d1的日期" << endl;
	}
}

int main()
{
	Test1();
	return 0;
}

🍌日期的加减天数计算

💦日期 + 天数
  • 思路:

对于日期 + 天数,我们得到的还是一个日期。特别需要注意进位的问题(天满了往月进,月满了往年进),主要考虑如下几个特殊点:

  • 加过的天数超过该月的最大天数,需要进位
  • 当月进位到13时,年进位+1,月置为1

  • 法一:
Date Date::operator+(int day) 
{
	Date ret(*this); //拷贝构造,拿d1去初始化ret
	ret._day += day;
	while (ret._day > GetMonthDay(ret._year, ret._month))
	{
		ret._day -= GetMonthDay(ret._year, ret._month);
		ret._month++;
		if (ret._month == 13)
		{
			ret._year++;
			ret._month = 1;
		}
	}
	return ret;
}

出了作用域,对象ret不在,它是一个局部对象,我们这里不能用引用,用了的话,返回的就是ret的别名,但是ret又已经销毁了,访问野指针了,所以出了作用域,如果对象不在了,就不能用引用返回,要用传值返回

  • 法二:复用日期+=天数

此法是建立在日期+=天数的基础上完成的,这里各位可以先看下文日期+=天数,然后我们进行复用:

Date Date::operator+(int day) 
{
	//法二:复用日期 += 天数
	Date ret(*this);
	ret += day;
	return ret;
}
  • 法一和法二熟优?

答案:法二更好,也就是用+去复用+=,具体原因在下文会解释。

💦日期 + = 天数

这里实现 += 其实有两种方案

  • 法一:

前面我实现的日期+天数,仔细观察我的代码,函数的第一行,我就调用了一个拷贝构造:

Date ret(*this); //拷贝构造,拿d1去初始化ret

这里调用拷贝构造,是为了不在*this本身上做变动,只在ret上进行操作,其理由是日期+天数得到的是另一个日期,而不用拷贝构造直接在*this上做改动只会导致原有的日期也变化,而这个变化正是我日期 += 天数的需求

仔细想想:+=天数就是在原有的日期上再加一定的天数,直接对*this做手脚即可,因此只需对日期+天数的代码进行小改动即可:

Date& Date::operator+=(int day) //传引用返回
{
	//如果day小于0,要单独处理
	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;
}

注意这里是传引用返回,原因就在于我返回的*this是全局的,出了作用域还在

  • 法二:复用日期 +天数
Date& Date::operator+=(int day)
{
	//法二:复用
	* this = *this + day; //让d1+过天数后再返回给自己从而实现+=
	return *this;
}
  • 法一和法二熟优?

答案:法一。其实讨论这个问题就是在讨论用**+去复用+=好还是用+=复用+好****,答案是用****+=去复用+好,因为+两次拷贝****,而****+=没有拷贝,所以实现****+,并且用+=去复用+**效率更高

💦 日期 -= 天数
  • 思路:

日期-=天数得到的还是一个日期,且是在原日期的基础上做改动。合法的日期减去天数后的day只要>0就没问题,若小于0就要借位了。要注意当减去的天数<0时单独讨论。具体步骤如下:

  1. 当减的天数为负数,则为+=,直接调用
  2. 若减后的day<0,月-1
  3. 若月 = 0,则年-1,月置为12

  • 代码如下:
// 日期-= 天数 :d1 d1 - 100
Date& Date::operator-=(int day)
{
	if (day < 0)
	{
		return *this += (-day);
	}
	_day -= day;
	while (_day <= 0)
	{
		_month--;
		if (_month == 0)
		{
			_month = 12;
			_year--;
		}
		_day += GetMonthDay(_year, _month);
	}
	return *this;
}
💦日期 - 天数

有了先前日期+和+=的基础,这里实现日期 - 天数直接复用日期 -= 天数即可:

// 日期-天数 : d1 - 100
Date Date::operator-(int day)
{
	// 调用拷贝构造
	Date temp(*this);
	// 复用 "-="
	temp -= day;
	return temp;
}
💦日期 前置++
  • 思路:

C++里有前置++和后置++,这就导致一个巨大的问题,该如何区分它们,具体实现过程不难(直接复用+=即可),难的是如何区分前置和后置。因此C++规定,无参的为前置,有参的为后置。

  • 代码如下:
// 前置++ (无参的为前置,有参的为后置)
Date& Date::operator++()
{
	// 直接复用 +=
	*this += 1;
	return *this;
}
💦日期 后置++
  • 思路:

有参的即为后置,后置++拿到的返回值应该是自己本身未加过的,因此要先把自己保存起来,再++*this,随后返回自己。

  • 代码如下:
// 后置++(无参的为前置,有参的为后置)
// int i 这里的形参可以写,可以不写
Date Date::operator++(int i)
{
	Date temp(*this);
	*this += 1;
	return temp;
}

💦日期 前置–
  • 思路:

前置–和前置++没啥区别,只不过内部复用的是-=

  • 代码如下:
// 前置--
Date& Date::operator--()
{
	*this -= 1;
	return *this;
}
💦日期 后置–
  • 思路:

后置–和后置++类似,只不过内部复用的是-=,不再赘述

  • 代码如下:
//后置--
Date Date::operator--(int i)
{
	Date temp(*this);
	*this -= 1;
	return temp;
}
⭐效果展示

举例:将日期 + 100 天  :2023 / 10 / 28  + 100

// d1+=100 测试
void Test3()
{
	Date d1(2023, 10, 28);
	cout << "d1的日期为:";
	d1.Printf();
	cout << endl;
	d1 += 100;
	cout << "d1的日期+100为:";
	d1.Printf();
}

int main()
{
	Test3();
	return 0;
}

🍊日期与日期之间的减法运算

💦日期 - 日期
  • 思路:

日期 - 日期得到的是天数,首先我们得判断两个日期的大小,用min和max代替小的和大的,随后,算出min和max之间的差距,若min!=max,则min就++,随即定义变量n也自增++,最后返回n(注意符号)

  • 代码如下:
// 日期 - 日期
int Date::operator-(const Date& d)
{
	// 方便后续计算正负
	int flag = 1;
	Date max = *this;
	Date min = d;
	// 确保max是大的  min是小的
	if (*this < d)
	{
		min = *this;
		max = d;
		flag = -1;  //计算正负
	}
	int n = 0;
	// 计算min和max之间的绝对值差距
	while (min != max)
	{
		min++;
		n++;
	}
	return n * flag;
}
⭐效果展示

举例:计算 2023 / 10 /28  -------------   2023  / 11 / 11  的天数差距

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

随即定义变量n也自增++,最后返回n(注意符号)

  • 代码如下:
// 日期 - 日期
int Date::operator-(const Date& d)
{
	// 方便后续计算正负
	int flag = 1;
	Date max = *this;
	Date min = d;
	// 确保max是大的  min是小的
	if (*this < d)
	{
		min = *this;
		max = d;
		flag = -1;  //计算正负
	}
	int n = 0;
	// 计算min和max之间的绝对值差距
	while (min != max)
	{
		min++;
		n++;
	}
	return n * flag;
}
⭐效果展示

举例:计算 2023 / 10 /28  -------------   2023  / 11 / 11  的天数差距

[外链图片转存中…(img-t9eqNBU0-1715751730162)]
[外链图片转存中…(img-yZa37nRd-1715751730163)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 17
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值