[C++]日期类的实现

本专栏内容为:C++学习专栏,分为初阶和进阶两部分。 通过本专栏的深入学习,你可以了解并掌握C++。

💓博主csdn个人主页:小小unicorn
⏩专栏分类:C++
🚚代码仓库:小小unicorn的代码仓库🚚
🌹🌹🌹关注我带你学习编程知识

C++初阶(二)

  • 日期类的实现:
    • 打印函数:
    • 构造函数
    • 赋值重载=
    • 日期 += 天数
    • 日期 + 天数
    • 日期 -= 天数
    • 日期 - 天数
    • 前置 ++
    • 后置 ++
    • 前置 --
    • 后置--
    • 日期类的大小关系比较
      • >运算符的重载
      • ==运算符的重载
      • >=运算符的重载
      • <运算符的重载
      • <=运算符的重载
      • !=运算符的重载
      • 日期 - 日期
    • 输出
    • 输入

日期类的实现:

本篇文章带大家实现一个日期类。

我们实现声明定义分离:

这是日期类中所包含的成员函数和成员变量:(Data.h)

class Date
{
     // 友元声明
friend ostream& operator<<(ostream& out, const Date& d);
friend istream& operator>>(istream& in, Date& d);
public:
	// 构造函数
	Date(int year = 0, int month = 1, int day = 1);
	// 打印函数
	void Print() const;
    //赋值重载
    Date& operator=(const Date& d);
	// 日期+=天数
	Date& operator+=(int day);
	// 日期+天数
	Date operator+(int day) const;
	// 日期-=天数
	Date& operator-=(int day);
	// 日期-天数
	Date operator-(int day) const;
	// 函数重载
     // 运算符重载
      // ++d1 -> d1.operator++()
	// 前置++
	Date& operator++();
	// d1++ -> d1.operator++(0)
   // 加一个int参数,进行占位,跟前置++构成函数重载进行区分
   // 本质后置++调用,编译器进行特殊处理
	// 后置++
	Date operator++(int);
	// 前置--
	Date& operator--();
	// 后置--
	Date operator--(int);
	// 日期的大小关系比较
	// 总结一下:只读函数可以加const,内部不涉及修改成员的都是只读函数
	bool operator>(const Date& d) const;
	bool operator>=(const Date& d) const;
	bool operator<(const Date& d) const;
	bool operator<=(const Date& d) const;
	bool operator==(const Date& d) const;
	bool operator!=(const Date& d) const;
	// 日期-日期
	int operator-(const Date& d) const;

	// 析构,拷贝构造,赋值重载可以不写,使用默认生成的即可

private:
	int _year;
	int _month;
	int _day;
};

打印函数:

// 打印函数
void Date::Print() const
{
	cout << _year << "年" << _month << "月" << _day << "日" << endl;
}

构造函数

进入构造函数体,首先需要检查日期的合法性,只有当日期合法时,才能进行后续的构造操作。

// 获取某年某月的天数
inline int GetMonthDay(int year, int month)
{
	// 数组存储平年每个月的天数
	static int dayArray[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
	int day = dayArray[month];
	if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
	{
		//闰年2月的天数
		day = 29;
	}
	return day;
}
// 构造函数
Date::Date(int year, int month, int day)
{
	_year = year;
    _month = month;
   _day = day;

// 检查日期是否合法
if (month < 1 || month > 12
	|| day < 1 || day > GetMonthDay(year, month))
{
	cout << "非法日期" << endl;
	// exit(-1);
}
	}
}

GetMonthDay函数中的三个细节:
 1.该函数可能被多次调用,所以我们最好将其设置为内联函数。
 2.函数中存储每月天数的数组最好是用static修饰,存储在静态区,避免每次调用该函数都需要重新开辟数组。
 3.逻辑与应该先判断month == 2是否为真,因为当不是2月的时候我们不必判断是不是闰年。

注意:当函数声明和定义分开时,在声明时注明缺省参数,定义时不标出缺省参数。

void TestDate1()
{
	Date d1(2024, 3, 23);
	Date d2(2023,3,23);
	
	d1.Print();
	d2.Print();
	
	Date d3(2010, 2, 29);
	d3.Print();
	
	Date d4(2023, 13, 29);
	d4.Print();
}

检查一下:
在这里插入图片描述

赋值重载=

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

测试一下:

//测试赋值重载
void TestDate2()
{
	Date d1(2024, 2, 22);
	// 拷贝构造,一个已经存在的对象去初始化另一个要创建对象
	Date d2(d1);
	d1.Print();
	d2.Print();
	Date d3(2023, 11, 15);
	// 赋值,两个已经存在对象进行拷贝
	//d1 = d3;  // d1.operator=(d3)
	d1 = d2 = d3;
	d1.Print();
	d2.Print();
	d3.Print();
}

测试结果:
在这里插入图片描述

日期 += 天数

对于+=运算符,我们先将需要加的天数加到日上面,然后判断日期是否合法,若不合法,则通过不断调整,直到日期合法为止。
调整日期的思路:
 1.若日已满,则日减去当前月的天数,月加一。
 2.若月已满,则将年加一,月置为1。
反复执行1和2,直到日期合法为止。

// 日期+=天数
Date& Date::operator+=(int day)
{
	if (day<0)
	{
		// 复用operator-=
		*this -= -day;
	}
	else
	{
		_day += day;
		// 日期不合法,通过不断调整,直到最后日期合法为止
		while (_day > GetMonthDay(_year, _month))
		{
			_day -= GetMonthDay(_year, _month);
			_month++;
			if (_month > 12)
			{
				_year++;
				_month = 1;
			}
		}
	}
	return *this;
}

注:当需要加的天数为负数时,转而调用-=运算符重载函数。

测试+=

//测试+=
void TestDate3()
{
	Date d1(2024, 2, 22);
	d1 += 200;
	d1.Print();
}

测试结果:
在这里插入图片描述

200天后是2024年9月9日,那么对不对呢?
我们用日期计算器计算一下:
在这里插入图片描述

发现没有问题。

日期 + 天数

+运算符的重载,我们可以复用上面已经实现的+=运算符的重载函数。但是要注意:虽然我们返回的是加了之后的值,但是对象本身的值并没有改变。就像a = b + 1,b + 1的返回值是b + 1,但是b的值并没有改变。所以我们还可以用const对该函数进行修饰,防止函数内部改变了this指针指向的对象。

// 日期+天数
Date Date::operator+(int day) const
{
	Date tmp(*this);// 拷贝构造tmp,用于返回
	// 复用operator+=
	tmp += day;
	return tmp;
}

注意:+=运算符的重载函数采用的是引用返回,因为出了函数作用域,this指针指向的对象没有被销毁。但+运算符的重载函数的返回值只能是传值返回,因为出了函数作用域,对象tmp就被销毁了,不能使用引用返回。

日期 -= 天数

对于-=运算符,我们先用日减去需要减的天数,然后判断日期是否合法,若不合法,则通过不断调整,直到日期合法为止。
调整日期的思路:
 1.若日为负数,则月减一。
 2.若月为0,则年减一,月置为12。
 3.日加上当前月的天数。
反复执行1、2和3,直到日期合法为止。

// 日期-=天数
Date& Date::operator-=(int day)
{
	if (day < 0)
	{
		// 复用operator+=
		*this += -day;
	}
	else
	{
		_day -= day;
		// 日期不合法,通过不断调整,直到最后日期合法为止
		while (_day <= 0)
		{
			_month--;
			if (_month == 0)
			{
				_year--;
				_month = 12;
			}
			_day += GetMonthDay(_year, _month);
		}
	}
	return *this;
}

注:当需要减的天数为负数时,转而调用+=运算符重载函数。

日期 - 天数

和+运算符的重载类似,我们可以复用上面已经实现的-=运算符的重载函数,而且最好用const对该函数进行修饰,防止函数内部改变了this指针指向的对象。

// 日期-天数
Date Date::operator-(int day) const
{
	Date tmp(*this);// 拷贝构造tmp,用于返回
	// 复用operator-=
	tmp -= day;
	return tmp;
}

注意:-=运算符的重载函数采用的是引用返回,但-运算符的重载函数的返回值只能是传值返回,也是由于-运算符重载函数中的tmp对象出了函数作用域被销毁了,所以不能使用引用返回。

测试-=

//测试-=
void TestDate4()
{
	Date d1(2024, 2, 22);
	d1 -= 200;
	d1.Print();
	
	Date d2(2024, 3, 22);
	d2 += -200;
	d2.Print();
	
	Date d3(2024, 9, 6);
	d3 -= -200;
	d3.Print();
}

测试结果:

在这里插入图片描述
在这里插入图片描述

前置 ++

前置++,我们可以复用+=运算符的重载函数。

// 前置++
Date& Date::operator++()
{
	// 复用operator+=
	*this += 1;
	return *this;
}

后置 ++

由于前置++和后置++的运算符均为++,为了区分它们的运算符重载,我们给后置++的运算符重载的参数加上一个int型参数,使用后置++时不需要给这个int参数传入实参,因为这里int参数的作用只是为了跟前置++构成重载。

// 后置++
Date Date::operator++(int)
{
	Date tmp(*this);// 拷贝构造tmp,用于返回
	// 复用operator+=
	*this += 1;
	return tmp;
}

注意:后置++也是需要返回加了之前的值,只能先用对象tmp保存之前的值,然后再然对象加一,最后返回tmp对象。由于tmp对象出了该函数作用域就被销毁了,所以后置++只能使用传值返回,而前置++可以使用引用返回。

测试一下:

void TestDate5()
{
	Date d1(2023, 11, 14);
	Date ret1 = d1++;
	//Date ret1 = d1.operator++(0);
	ret1.Print();
	d1.Print();

	Date ret2 = ++d1;
	//Date ret2 = d1.operator++();
	ret2.Print();
	d1.Print();
}

测试结果:
在这里插入图片描述

前置 –

前置–,我们也是可以复用前面的-=运算符的重载函数。

// 前置--
Date& Date::operator--()
{
	// 复用operator-=
	*this -= 1;
	return *this;
}

后置–

后置–需要注意的事项和后置++是一样的,我这里就不过多阐述了。

// 后置--
Date Date::operator--(int)
{
	Date tmp(*this);// 拷贝构造tmp,用于返回
	// 复用operator-=
	*this -= 1;
	return tmp;
}

日期类的大小关系比较

日期类的大小关系比较需要重载的运算符看起来有6个,实际上我们只用实现两个就可以了,然后其他的通过复用这两个就可以实现。

注意:进行日期的大小比较,我们并不会改变传入对象的值,所以这6个运算符重载函数都应该被const所修饰。

>运算符的重载

>运算符的重载很简单,先判断年是否大于,再判断月是否大于,最后判断日是否大于,这其中有一者为真则函数返回true,否则返回false。

bool Date::operator>(const Date& d) const
{
	if (_year > d._year)
	{
		return true;
	}
	else if (_year == d._year)
	{
		if (_month > d._month)
		{
			return true;
		}
		else if (_month == d._month)
		{
			if (_day > d._day)
			{
				return true;
			}
		}
	}
	return false;
}

==运算符的重载

==运算符的重载也是很简单,年月日均相等,则为真。

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

>=运算符的重载

 >=,即大于或者等于,满足其中之一即可。
bool Date::operator>=(const Date& d) const
{
	return *this > d || *this == d;
}

<运算符的重载

<,大于等于的反面即是小于。

bool Date::operator<(const Date& d) const
{
	return !(*this >= d);
}

<=运算符的重载

<=,大于的返回即是小于等于。

bool Date::operator<=(const Date& d) const
{
	return !(*this > d);
}

!=运算符的重载

!=,等于的反面即是不等于。

bool Date::operator!=(const Date& d) const
{
	return !(*this == d);
}

日期 - 日期

日期 - 日期,即计算传入的两个日期相差的天数。我们只需要让较小的日期的天数一直加一,直到最后和较大的日期相等即可,这个过程中较小日期所加的总天数便是这两个日期之间差值的绝对值。若是第一个日期大于第二个日期,则返回这个差值的正值,若第一个日期小于第二个日期,则返回这个差值的负值。

// 日期-日期
int Date::operator-(const Date& d) const
{
	Date max = *this;// 假设第一个日期较大
	Date min = d;// 假设第二个日期较小
	int flag = 1;// 此时结果应该为正值
	if (*this < d)
	{
		// 假设错误,更正
		max = d;
		min = *this;
		flag = -1;// 此时结果应该为负值
	}
	int n = 0;// 记录所加的总天数
	while (min != max)
	{
		min++;// 较小的日期++
		n++;// 总天数++
	}
	return n*flag;
}

测试一下:
在这里插入图片描述
blog.csdnimg.cn/6b4338f019944d85a35f4c08362ec4d7.png)

代码中使用flag变量标记返回值的正负,flag为1代表返回的是正值,flag为-1代表返回的是负值,最后返回总天数与flag相乘之后的值即可。

输出

ostream& operator<<(ostream& out, const Date& d)
{
	out << d._year << "/" << d._month << "/" << d._day << endl;

	return out;
}

输入

istream& operator>>(istream& in, Date& d)
{
	in >> d._year >> d._month >> d._day;
	return in;
}

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

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

相关文章

【TB作品】430单片机,单片机串口多功能通信,Proteus仿真

文章目录 题目功能仿真图程序介绍代码、仿真、原理图、PCB 题目 60、单片机串口多功能通信 基本要求: 设计一串口通信程序,波特率38400,通过RS232与PC机通信。 自动循环发送数据串(设计在程序中) 接收并存储和显示该数据串 在发送端定义10个ASCII码键0-9 按键发送单字节,PC机接…

网络上常见的环路指的是什么

人类的创造力与破坏力同样强大"。 网路互通&#xff0c;同样也衍生出纷繁复杂的路由协议和各种因特网服务&#xff0c;以及"网络安全"这个庞大的领域。 这也是为什么说当今所有的网络通讯流量中&#xff0c;80%的资源都被浪费&#xff0c;只有20%被用以有效数…

AXS4004 5V 300mA 低噪声电荷泵 DCDC转换器 爱协生 参数文

概述 AXS4004是一款低噪声、固定频率360KHz的电荷泵型DC DC转换器&#xff0c;在输入电压2.5V到5V的情况下&#xff0c;恒定输出5V电压&#xff0c;电压精度为&#xff1a;3%&#xff0c;输出电流最大达到300mA。AXS4004外部零件少&#xff0c;非常适合小型的电池供电应用。AX…

【索引失效】MySQL索引失效场景

1、对索引使用左或者左右模糊匹配 当我们使用左或者左右模糊匹配的时候&#xff0c;也就是 like %xx 或者 like %xx% 这两种方式都会造成索引失效。 比如下面的 like 语句&#xff0c;查询 name 后缀为「林」的用户&#xff0c;执行计划中的 typeALL 就代表了全表扫描&#xff…

【VTKExamples::Points】第六期 ExtractSurface

很高兴在雪易的CSDN遇见你 VTK技术爱好者 QQ:870202403 公众号:VTK忠粉 前言 本文分享VTK样例ExtractSurface,并解析接口vtkSignedDistance & vtkExtractSurface,希望对各位小伙伴有所帮助! 感谢各位小伙伴的点赞+关注,小易会继续努力分享,一起进步! 你的…

leetcode 225.用队列实现栈 JAVA

题目 思路 1.一种是用双端队列&#xff08;Deque&#xff09;&#xff0c;直接就可以调用很多现成的方法&#xff0c;非常方便。 2.另一种是用普通的队列&#xff08;Queue&#xff09;,要实现栈的先入后出&#xff0c;可以将最后一个元素的前面所有元素出队&#xff0c;然后…

LLM漫谈(五)| 从q star视角解密OpenAI 2027年实现AGI计划

最近&#xff0c;网上疯传OpenAI2027年关于AGI的计划。在本文&#xff0c;我们将针对部分细节以第一人称进行分享。​ 摘要&#xff1a;OpenAI于2022年8月开始训练一个125万亿参数的多模态模型。第一个阶段是Arrakis&#xff0c;也叫Q*&#xff0c;该模型于2023年12月完成训练&…

如何做好软件架构

最近学习了Udemy的一个软件架构课程&#xff0c;在此做一个记录和分享。 总的来说&#xff0c;软件架构是基于实际业务需求&#xff0c;无法为实际业务服务&#xff0c;再花哨的软件架构都无法产生任何价值。 当需求到来&#xff0c;我们需要分以下几个大致步骤进行分析和拆解…

JAVA_Tomcat

Tomcat 使用教程 1.下载: http://tomcat.apache.org/ 2.安装: 解压压缩包(安装目录不要有中文) 3.卸载: 删除目录即可 4.启动: 运行./bin/startup.sh1.黑窗口一闪而过: 没有配置好JDK环境变量2.启动报错(查看日志文件): 端口占用 5.关闭: 1.强制关闭: 点击窗口关闭按钮2.正常…

PTA 抢红包 25分 (JAVA)

题目描述 没有人没抢过红包吧…… 这里给出N个人之间互相发红包、抢红包的记录&#xff0c;请你统计一下他们抢红包的收获。 输入格式&#xff1a; 输出格式&#xff1a; 按照收入金额从高到低的递减顺序输出每个人的编号和收入金额&#xff08;以元为单位&#xff0c;输出小…

【循环神经网络rnn】一篇文章讲透

目录 引言 二、RNN的基本原理 代码事例 三、RNN的优化方法 1 长短期记忆网络&#xff08;LSTM&#xff09; 2 门控循环单元&#xff08;GRU&#xff09; 四、更多优化方法 1 选择合适的RNN结构 2 使用并行化技术 3 优化超参数 4 使用梯度裁剪 5 使用混合精度训练 …

147 Linux 网络编程3 ,高并发服务器 --多路I/O转接服务器 - select

从前面的知识学习了如何通过socket &#xff0c;多进程&#xff0c;多线程创建一个高并发服务器&#xff0c;但是在实际工作中&#xff0c;我们并不会用到前面的方法 去弄一个高并发服务器&#xff0c;有更加好用的方法&#xff0c;就是多路I/O转接器 零 多路I/O转接服务器 多…

Leetcode739. 每日温度

Every day a Leetcode 题目来源&#xff1a;739. 每日温度 解法1&#xff1a;单调栈-从左到右 单调栈中记录还没算出「下一个更大元素」的那些数&#xff08;的下标&#xff09;。 代码&#xff1a; /** lc appleetcode.cn id739 langcpp** [739] 每日温度*/// lc codesta…

深入理解指针03

1. 字符指针变量 在指针的类型中我们知道有⼀种指针类型为字符指针char*; ⼀般使⽤: int main(){char ch w;char *pc &ch;*pc w;return 0;} 还有⼀种使⽤⽅式如下: int main() {const char* pstr "hello world";//这⾥是把⼀个字符串放到pstr指针变量⾥了…

【Linux实践室】Linux用户管理实战指南:新建与删除用户操作详解

&#x1f308;个人主页&#xff1a;聆风吟_ &#x1f525;系列专栏&#xff1a;Linux实践室、网络奇遇记 &#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 文章目录 一. ⛳️任务描述二. ⛳️相关知识2.1 &#x1f514;Linux创建用户命令2.1.1 知识点讲解2.1.2 案…

OceanMind海睿思入选中国信通院《2023高质量数字化转型技术解决方案集》

近日&#xff0c;由中国信息通信研究院“铸基计划”编制的《2023高质量数字化转型技术解决方案集&#xff08;第一版&#xff09;》正式发布。 中新赛克海睿思 凭借卓越的产品力以及广泛的行业实践&#xff0c;成功入选该方案集的数据分析行业技术解决方案。 为促进数字化转型…

探索uni-app项目的架构与开发实践:快速开发的项目模板参考

摘要&#xff1a;本文将深入探讨uni-app项目架构的模板设计&#xff0c;以及如何通过使用该模板实现快速开发。我们将重点介绍模板中的组件示例、SDK示例和模板页面&#xff0c;并阐述它们在提高开发效率和优化用户体验方面的作用。 一、引言 随着移动互联网的迅猛发展&#…

每日一题 --- 209. 长度最小的子数组[力扣][Go]

长度最小子数组 题目&#xff1a; 给定一个含有 n 个正整数的数组和一个正整数 target 。 找出该数组中满足其总和大于等于 target 的长度最小的 连续 子数组 [numsl, numsl1, ..., numsr-1, numsr] &#xff0c;并返回其长度**。**如果不存在符合条件的子数组&#xff0c…

Learn OpenGL 19 几何着色器

几何着色器 在顶点和片段着色器之间有一个可选的几何着色器(Geometry Shader)&#xff0c;几何着色器的输入是一个图元&#xff08;如点或三角形&#xff09;的一组顶点。几何着色器可以在顶点发送到下一着色器阶段之前对它们随意变换。然而&#xff0c;几何着色器最有趣的地方…

4.KubeSphereV3.4-DevOps配置maven私服

1.修改方法 maven容器模板中用的是中央仓库打包&#xff0c;但是我们打包需要用到私服。那么我们需要将私服配置到容器settings.xml中。 以 admin账号登录 在配置字典里找到 ks-devops-agent 将MavenSetting中mirror标签里配上私服地址&#xff1a; <?xml version"…