日期类(Date)的实现 (C++版)

news2024/12/23 8:33:52

                   

                       

                                         🌹个人主页🌹:喜欢草莓熊的bear

                                                🌹专栏🌹:C++入门

 

目录

前言

一、Date的头文件,包含函数声明

二、 Date.cpp

2.1 int GetMonthDay(int year, int month)

2.2 bool Check()

2.3 Date& operator+=(int day)

2.4 Date& operator-=(int day)

2.5 Date& operator++()

2.6 Date operator++(int)

2.7 bool operator < (const Date& d)const

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

2.9 int operator-(const Date& d)const

2.10 ostream& operator<<(ostream& out, const Date& d)

2.11 istream& operator>>(istream& i, Date& d)

三、 完整代码

Date.cpp

总结


前言

hello ,大家又来跟着bear学习了。一起奔向更好的自己

一、Date的头文件,包含函数声明

Date.h

#pragma once
#include<iostream>
#include<cmath>
#include <cassert>
using namespace std;

class Date

{
public:

	friend ostream& operator<<(ostream& out, const Date& d);//友源函数的声明
	friend istream& operator>>(istream& in, Date& d);//友源函数的声明
	// 获取某年某月的天数
	int GetMonthDay(int year, int month)
	{
		assert(month > 0 && month < 13);
		static int arr[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
		int day = arr[month];
		if (month == 2 && (year % 4 == 0 && year % 100 != 0 || year % 400 == 0))//润年2月多一天
		{
			day++;
		}

		return day;
	}

	// 全缺省的构造函数
	Date(int year = 1949,int month = 10,int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;

		if (!Check())
		{
			cout << "非法日期输入!";
			Print();
		}
	}

	void Print()const;//打印函数

	bool Check();//检查函数

	// 全缺省的构造函数

	// 拷贝构造函数
  // d2(d1)
	Date(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

	// 赋值运算符重载
  // d2 = d3 -> d2.operator=(&d2, d3)
	//Date& operator=(const Date& d);

	// 析构函数
	~Date()
	{
		_year = 2024;
		_month = 1;
		_day = 1;
	}

	// 日期+=天数
	Date& operator+=(int day);

	// 日期+天数
	Date operator+(int day)const;

	// 日期-天数
	Date operator-(int day)const;

	// 日期-=天数
	Date& operator-=(int day);

	// 前置++
	Date& operator++();

	// 后置++
	Date operator++(int);

	// 后置--
	Date operator--(int);

	// 前置--
	Date& operator--();

	// >运算符重载
	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;

	//void operator<<(ostream& out):


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

ostream& operator<<(ostream& out, const Date& d);//输出流操作符重载
istream& operator>>(istream& i, Date& d);//输入流操作符重载

二、 Date.cpp

2.1 int GetMonthDay(int year, int month)

GetMonthDay函数的功能是:// 获取某年某月的天数

其中就要考虑到润年的二月比正常的多一天,这里我们的想法是使用一个静态数组来储存每个月的天数。

static int arr[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };

这里多定义一个空间,方便下标和月份对应。 

闰年的判断条件我们在C语言里面就学习过了,那就是能被4整除且不能被100整除 或者 被400整除。转换成代码就是

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

最主要的两个功能已经实现了我们就完整的实现一下这个获取某年某月的天数函数

// 获取某年某月的天数
int GetMonthDay(int year, int month)
{
	assert(month > 0 && month < 13);
	static int arr[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
	int day = arr[month];
	if (month == 2 && (year % 4 == 0 && year % 100 != 0 || year % 400 == 0))//润年2月多一天
	{
		++day;
	}

	return day;
}

 这里使用静态的数组原因就是在调用该函数的时候不用多次申请空间。因为静态是结束是程序周期结束。

还有把month = 2 也是使得程序更加高效。这个函数还是很简单的。

2.2 bool Check()

bool Check()作用:检查日期是否合法,因为不可能出现6月31日的日期。

检查的对象有month 和 day。

_month < 1 ||  _month > 12


_day < 1 ||  _day > Date::GetMonthDay(_year, _month)
bool Date::Check()//检查日期是否合法
{
	if (_month < 1 || _month > 12 || _day < 1
		|| _day > Date::GetMonthDay(_year, _month))
	{
		return false;
	}
	else
	{
		return true;
	}
}

2.3 Date& operator+=(int day)

Date& operator+=(int day)实现:日期+=天数 ,得到一个新的日期

思路很简单就是进位的思想:满了这个月的天数就进到下一个月。

Date& Date::operator+=(int day)
{
	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;
}

 为了防止有吃饱了没事做的人写负数,所以我们还添加了一个放回措施,加一个负的天数就相当于减掉天数。根据+=我们就可以推到日期+天数这个函数。

// 日期+天数
Date Date::operator+(int day)const
{
	Date tmp = *this;
	tmp += day;
	return tmp;
}

这里面的“  +=  ”就是我们自己写的+=函数,也是+=运算符的重载函数。这里加cosnt是为了应付cosnt类型的数据。 

2.4 Date& operator-=(int day)

Date& operator-=(int day):日期-=天数。

实现思路与+=相似,采用退位的方式。这个月的天数不够就借用前一个月的天数。

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

2.5 Date& operator++()

Date& operator++():前置++运算符的重载实现,前置++特点,先++后引用

Date& operator++()
{
 *this+=1;
 return *this;
}

2.6 Date operator++(int)

Date operator++(int):后置++特点先引用后++,为了分清楚前置++还是后置++,祖师爷定义了后置++在运算符重载时会多一个没有任何作用的int 参数(作用知道这个是后置++)

// 后置++
Date Date::operator++(int)
{
	Date tmp = *this;
	*this += 1;
	return tmp;
}

     // 后置--   Date operator--(int);      // 前置--    Date& operator--();

大家可以自己实现--的功能,道理一样。不会实现的电疗伺候。

// 后置--
Date Date::operator--(int)
{
	Date tmp = *this;
	*this -= 1;
	return tmp;
}
// 前置--
Date& Date::operator--()
{
	*this -= 1;
	return *this;
}

2.7 bool operator < (const Date& d)const

bool operator < (const Date& d)const:比较日期的大小

思路:依次比较 先比较年 再比较月 再比较日 。这里我们使用逆向思维成立的为条件。

// d1 < d2
// <运算符重载
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)
	{
		return _day == d._day;
	}
	return false;
}

 

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

 bool operator==(const Date& d)const:日期相等

思路还是延续之前的比较大小的。

// ==运算符重载
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);
}

// >=运算符重载
bool Date::operator >= (const Date& d)const
{
	return !(*this < d);
}

// <=运算符重载
bool Date::operator <= (const Date& d)const
{
	return *this < d || *this == d;
}
// 
// !=运算符重载
bool Date::operator != (const Date& d)const
{
	return !(*this == d);
}

2.9 int operator-(const Date& d)const

int operator-(const Date& d)const:实现两个日期相减。

思路1:依靠着进位和退位来计算差值。(不能确定相差的月是哪几个月)。

思路2:直接通过计算来达到相同的日期(有人会说这样效率低下,但是我们的cpu一秒跑很快所以不用担心这些效率)

我们这里采用思路2

// 日期-日期 返回天数
int Date::operator-(const Date& d)const
{
	int flag = 1;
	Date max = *this;
	Date min = d;
	if (max < min)
	{
		max = d;
		min = *this;
		flag = -1;
	}
	int n = 0;
	while (min != max)
	{
		++min;
		++n;
	}

	return n*flag;
}

这个代码大部分都很普通,点睛之笔在这个flag他防止用小日期-大日期得到正天数的麻烦。 

2.10 ostream& operator<<(ostream& out, const Date& d)

ostream& operator<<(ostream& out, const Date& d):输出流操作符重载

我们没有把这两个函数写道类里面,因为类的成员函数会把第一个参数直接给到this指针,这样不方便我们的正常的写作。所以我们就写到了类外面,但是这样我们就使用不了类里面的私有变量了。有一个解决方案,那就是友元函数的声明在类里面。

类的友元函数是定义在类外部,但有权访问类的所有私有(private)成员和保护(protected)成员。尽管友元函数的原型有在类的定义中出现过,但是友元函数并不是成员函数。

友元可以是一个函数,该函数被称为友元函数;友元也可以是一个类,该类被称为友元类,在这种情况下,整个类及其所有成员都是友元。

friend ostream& operator<<(ostream& out, const Date& d);//友元函数的声明

ostream& operator<<(ostream& out, const Date& d)//写成友源函数就可以访问到类里面的私有变量了
//这里类型改成ostream是方便多次输出
{
	out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
	return out;
}

这里返回类型写成了 ostream是方便多次输出,因为输出流就是这个类型。 

2.11 istream& operator>>(istream& i, Date& d)

istream& operator>>(istream& i, Date& d): //输入流操作符重载

与上面同理写成友元函数

friend istream& operator>>(istream& in, Date& d);//友元函数的声明

istream& operator>>(istream& in ,Date& d)//这里类型改成istream是方便多次输入
{
	while (1)
	{
		cout << "请输入日期 > :";
		in >> d._year >> d._month >> d._day;
		if (!d.Check())
		{
			cout << "非法日期!";
			d.Print();
			cout << "重新输入日期";
		}
		else
		{
			break;
		}
	}

	return in;
}

三、 完整代码

Date.cpp

Date.cpp
#define _CRT_SECURE_NO_WARNINGS
#include"Date.h"
// 日期+=天数
Date& Date::operator+=(int day)
{
	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;
}

bool Date::Check()//检查日期是否合法
{
	if (_month < 1 || _month > 12 || _day < 1
		|| _day > Date::GetMonthDay(_year, _month))
	{
		return false;
	}
	else
	{
		return true;
	}
}

void Date::Print()const
{
	cout << _year << "/" << _month << "/" << _day << endl;
}

// 日期+天数
Date Date::operator+(int day)const
{
	Date tmp = *this;
	tmp += day;
	return tmp;
}

// 日期-天数
Date Date::operator-(int day)const
{
	Date tmp = *this;
	tmp -= day;
	return tmp;
}

// 日期-=天数
Date& Date::operator-=(int day)
{
	if (day < 0)
	{
		return *this += (-day);
	}
	_day -= day;
	while (_day <= 0)
	{
		--_month;
		if (_month == 0)
		{
			_year--;
			_month = 12;
		}
		_day += GetMonthDay(_year, _month);
	}
	return *this;
}
// 前置++
Date& Date::operator++()
{
	*this += 1;
	return *this;
}
// 后置++
Date Date::operator++(int)
{
	Date tmp = *this;
	*this += 1;
	return tmp;
}
// 后置--
Date Date::operator--(int)
{
	Date tmp = *this;
	*this -= 1;
	return tmp;
}
// 前置--
Date& Date::operator--()
{
	*this -= 1;
	return *this;
}
// >运算符重载
bool Date::operator>(const Date& d)const
{
	return !(*this <= d);
}
// ==运算符重载
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);
}

// d1 < d2
// <运算符重载
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)
	{
		return _day == d._day;
	}
	return false;
}

// <=运算符重载
bool Date::operator <= (const Date& d)const
{
	return *this < d || *this == d;
}
// 
// !=运算符重载
bool Date::operator != (const Date& d)const
{
	return !(*this == d);
}
// 
// 日期-日期 返回天数
int Date::operator-(const Date& d)const
{
	int flag = 1;
	Date max = *this;
	Date min = d;
	if (max < min)
	{
		max = d;
		min = *this;
		flag = -1;
	}
	int n = 0;
	while (min != max)
	{
		++min;
		++n;
	}

	return n*flag;
}

//void operator<<(ostream& out)//this是当前类的类型。
//{
//	out << _year << "年" << _month << "月" << _day << "日" << endl;
//}

ostream& operator<<(ostream& out, const Date& d)//写成友源函数就可以访问到类里面的私有变量了
//这里类型改成ostream是方便多次输出
{
	out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
	return out;
}

istream& operator>>(istream& in ,Date& d)//这里类型改成istream是方便多次输入
{
	while (1)
	{
		cout << "请输入日期 > :";
		in >> d._year >> d._month >> d._day;
		if (!d.Check())
		{
			cout << "非法日期!";
			d.Print();
			cout << "重新输入日期";
		}
		else
		{
			break;
		}
	}

	return in;
}

test.cpp

#define _CRT_SECURE_NO_WARNINGS
#include"Date.h"
int main()
{
	/*Date s1(200, 10, 3);
	s1.Print();*/


	/*Date s2(2005, 10, 1);
	s2.Print();*/
	//cout << s1 - s2 << endl;
	//s1 -= 20;
	// s1.Print();测试减法
	//s1 += 20;
	//s1.Print();测试加法
	//Date s2 = ++s1;//测试前置++
	//s2.Print();
	//s1.Print();

	//s1 << cout;//和我们平常写的太不一样,放弃。

	//cout << s2 << s1 << endl;//这样就非常适合我们平常的写作习惯了

	Date d1;
	cin >> d1;

	cout << d1 + 50;

	return 0;
}

 

总结

感谢大家的支持,我会继续努力创造出更好的博客。

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

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

相关文章

基于YOLOv8-deepsort算法的智能车辆目标检测车辆跟踪和车辆计数

关于深度实战社区 我们是一个深度学习领域的独立工作室。团队成员有&#xff1a;中科大硕士、纽约大学硕士、浙江大学硕士、华东理工博士等&#xff0c;曾在腾讯、百度、德勤等担任算法工程师/产品经理。全网20多万粉丝&#xff0c;拥有2篇国家级人工智能发明专利。 社区特色…

HTB:Funnel[WriteUP]

目录 连接至HTB服务器并启动靶机 1.How many TCP ports are open? 2.What is the name of the directory that is available on the FTP server? 3.What is the default account password that every new member on the "Funnel" team should change as soon a…

cudnn8编译caffe过程(保姆级图文全过程,涵盖各种报错及解决办法)

众所周知,caffe是个较老的框架,而且只支持到cudnn7,但是笔者在复现ds-slam过程中又必须编译caffe,我的cuda版本是11.4,最低只支持到8.2.4,故没办法,只能编译了 在此记录过程、报错及解决办法如下; 首先安装依赖: sudo apt-get install git sudo apt-get install lib…

李宏毅 X 苹果书 自注意力机制 学习笔记下

b1 &#xff0c;b2...不是依序产生&#xff0c;而是同时被计算好的 从矩阵乘法角度看待self-attention运作过程 矩阵运算表示每一个a都要产生 a k v的操作如下&#xff1a; 矩阵运算表示的计算如下&#xff1a; A‘是A的normalization &#xff0c;用softmax 矩阵运算表示b计…

Ubuntu有关redis的命令

防火墙&#xff1a; systemctl status firewalld systemctl stop firewalld systemctl disable firewalld.service ifconfig查看ip地址 redis.conf在/etc/redis下&#xff0c;但是得sudo -i进入root模式 进入/etc/redis下开启redis-server服务 查看6379端口是否可以访问 net…

vue3- antd design vue 引入iconfont

文章目录 前言一、新建iconfont项目 前言 vue3项目中&#xff0c;如何引入第三方的iconfont的图标 一、新建iconfont项目 搜索需要的图标&#xff0c;加入购物车&#xff0c;购物车中图片加入项目 下载项目文件&#xff0c;打开压缩包后&#xff0c;将iconfont.js 文件拷贝到…

基于vue框架的大学生心理健康服务平台mwavu(程序+源码+数据库+调试部署+开发环境)系统界面在最后面。

系统程序文件列表 项目功能&#xff1a;学生,心理专家,心理咨询,健康文章,咨询回复,心理案例,监测预警,解压游戏,放松音乐 开题报告内容 基于Vue框架的大学生心理健康服务平台开题报告 一、研究背景与意义 随着社会的快速发展和教育竞争的日益激烈&#xff0c;大学生面临着…

SpringBoot教程(二十四) | SpringBoot实现分布式定时任务之Quartz

SpringBoot教程&#xff08;二十四&#xff09; | SpringBoot实现分布式定时任务之Quartz 简介适用场景Quartz核心概念Quartz 存储方式Quartz 版本类型引入相关依赖方式一&#xff1a;内存方式(MEMORY)存储实现定时任务1. 定义任务类2. 定义任务描述及创建任务触发器3. Quartz的…

VirtualBox Ubuntu22.04 NOI linux2.0 Terminal无法打开 终端打不开 两步解决法儿

新安装的虚拟机无法打开Terminal&#xff0c;从应用列表中单击Terminal&#xff0c;左上角任务栏会出现Terminal&#xff0c;并且鼠标转圈&#xff0c;但是过一会左上角Terminal消失&#xff0c;就像一切都没有来过。 解决办法&#xff1a; CTRL ALT F3 进入命令行模式&…

【自然语言处理】补充:基于向量空间的分类器

【自然语言处理】补充:基于向量空间的分类器 文章目录 【自然语言处理】补充:基于向量空间的分类器1. 特征选择2. 基于向量空间的分类方法3. Rocchio4. KNN5. 线性分类器1. 特征选择 特征选择 文本分类中,通常要将文本表示在一个高维空间下,每一维对应一个词项许多维上对应…

如何基于vite实现清除特定环境下的console和debugger

一、解决方法 方法一&#xff1a;使用esbuild 直接在vite.config.ts文件中写&#xff0c;无需下载插件 export default defineConfig(({ mode }) > {// 环境变量const env loadEnv(mode, root, "");return {base: env.VITE_PUBLIC_PATH,plugins: [vue(),...],…

中国书法-孙溟㠭浅析碑帖《九成宫醴泉铭》

中国书法孙溟㠭浅析碑帖《九成宫醴泉铭》 《九成宫醴泉铭》是由魏征撰文、欧阳询书丹&#xff0c;唐贞观六年&#xff08;公元632年&#xff09;立碑&#xff0c;篆书体题碑额。内容记载了唐太宗李世民在九成宫避暑山庄发现涌泉的事。 书法法度森严&#xff0c;腴润中见峭劲&…

图文并茂解释水平分表,垂直分表,水平分库,垂直分库

文章目录 1.垂直角度(表结构不一样)垂直分表:垂直分库&#xff1a; 2.水平角度(表结构一样)水平分库&#xff1a;水平分表&#xff1a; 1.垂直角度(表结构不一样) 垂直分表: 将一个表字段拆分成多个表&#xff0c;每个表存储部分字段。好处是避免IO时锁表的次数&#xff0c;分…

数据结构——遍历二叉树

目录 什么是遍历二叉树 根据遍历序列确定二叉树 例题&#xff08;根据先序中序以及后序中序求二叉树&#xff09; 遍历的算法实现 先序遍历 中序遍历 后序遍历 遍历算法的分析 二叉树的层次遍历 二叉树遍历算法的应用 二叉树的建立 复制二叉树 计算二叉树深度 计算二…

java发起POST方法请求第三方接口(编码处理)

文章目录 引言I 案例查询船舶轨迹配置JVM编码参数请求提供方常见问题II 工具类III 知识扩展:程序运行源代码各个阶段对编码的处理Java源码--->字节码Java字节码--->虚拟机--->操作系统操作系统-->显示设备引言 使用场景: 调用第三方平台接口 I 案例 查询船舶…

【MySQL】--数据类型

文章目录 1. 选择数据库1.1 语法 2. 查询当前选中的数据库2.1 语法 3. 常见数据类型分类4. 数据值类型4.1 类型列表4.2 数据类型取值范围 5. 字符串类型5.1 类型列表5.2 关于排序5.3 CHAR和VARCHAR的区别5.4 如何选择CHAR和VARCHAR5.5 VARCHAR与TEXT的区别 6. 日期类型6.1 类型…

基于SSM的仿win10界面的酒店管理系统

基于SSM的仿win10界面的酒店管理系统 运行环境: jdk1.8 eclipse tomcat7 mysql5.7 项目技术: jspssm&#xff08;springspringmvcmybatis&#xff09;mysql 项目功能模块&#xff1a;基础功能、房间类型、楼层信息、附属功能

重学SpringBoot3-集成Redis(六)之消息队列

更多SpringBoot3内容请关注我的专栏&#xff1a;《SpringBoot3》 期待您的点赞&#x1f44d;收藏⭐评论✍ 重学SpringBoot3-集成Redis&#xff08;六&#xff09;之消息队列 1. 什么是发布/订阅&#xff08;Pub/Sub&#xff09;&#xff1f;2. 场景应用3. Spring Boot 3 整合 R…

EtherNet/IP 转 EtherNet/IP, EtherCAT/Ethernet/IP/Profinet/ModbusTCP协议互转工业串口网关

EtherCAT/Ethernet/IP/Profinet/ModbusTCP协议互转工业串口网关https://item.taobao.com/item.htm?ftt&id822721028899 协议转换通信网关 EtherNet/IP 转 EtherNet/IP GW系列型号 MS-GW22 概述 简介 MS-GW22 是 EtherNet/IP 和 EtherNet/IP 协议转换网关&#xff0c;…

4.扩散模型的似然函数最大化(1)

1.似然函数最大化 扩散模型的训练目标是负的对数似然的一个变分下界(VLB)。在本节中&#xff0c;我们总结并调查最近关于扩散模型的似然最大化的工作。首先我们介绍似然函数最大化的意义&#xff0c;然后重点讨论3种类型的方法:噪声调度优化、逆向方差学习和精确的对数似然估计…