类和对象实操之【日期类】
✨个人主页: Yohifo
🎉所属专栏: C++修行之路
🎊每篇一句: 图片来源
The pessimist complains about the wind; the optimist expects it to change; the realist adjusts the sails.
- 悲观主义者抱怨风;乐观主义者期望它改变;现实主义者调整风帆。
文章目录
- 🗓️前言
- 🗓️正文
- 📆类的定义
- 📅合法性检验
- 📅判断闰年
- 📅获取年份天数
- 📅获取月份天数
- 📆运算符重载
- 📅判断等于
- 📅判断小于
- 📅复用至所有判断
- 📅重载流插入、提取
- 📆日期+=天数
- 📅核心思想
- 📅代码实现
- 📆日期-日期
- 📅核心思想
- 📅代码实现
- 📆自加、自减操作
- 📅前置
- 📅后置
- 📆程序源码
- 🗓️总结
🗓️前言
在学完类和对象
相关知识后,需要一个程序来供我们练习、巩固知识点,日期类
就是我们练习的首选程序,日期类
实现简单且功能丰富,相信在完整地将日期类
实现后,能对类和对象
有更好的掌握及更深的理解
🗓️正文
为了更符合工程标准,这里采用三个文件的方式实现程序
用于声明类和方法的 .h
头文件
Date.h
用于实现类和方法的 .cpp
源文件
Date.cpp
用于测试功能的 .cpp
源文件
test.cpp
📆类的定义
先简单定义一下每个类中都有的默认成员函数
//当前位于文件 Date.h 中
#pragma once
#include<iostream>
using std::cout; //采用部分展开的方式
using std::cin;//采用命名空间
namespace Yohifo
{class Date{public://构造函数,频繁使用且短小的代码直接在类的声明中实现,成为内联函数Date(int year = 2023, int month = 2, int day = 11):_year(year), _month(month), _day(day){}//拷贝构造函数Date(const Date& d){_year = d._year;_month = d._month;_day = d._day;}//赋值重载函数Date& operator=(const Date& d){if (this == &d)return *this;_year = d._year;_month = d._month;_day = d._day;return *this;}//析构函数~Date(){_year = 1970;_month = 2;_day = 11;}private:int _year; //年、月、日int _month;int _day;};
}
📅合法性检验
首先编写第一个函数:合法性检验
检验标准
- 年不能为0
- 月在区间 [1, 12] 内,超过为非法
- 根据年月推算出天数,天数不能操作规定天数,也不能
<= 0
注意:
- 当前包括后续函数都是采取先在头文件
Date.h
的类中声明,再到Date.cpp
实现的路径 - 因历史原因导致的闰年变动这里不考虑,该程序实现的是理想情况下的闰年状态
- 程序计算范围覆盖至公元前,限度为
[INT_MIN, INT_MAX]
#include"Date.h"using namespace Yohifo; //全局展开命名空间//合法性检验
bool Date::check() const
{//年不能为0年if (_year == 0)return false;//月份区间 [1, 12]if (_month < 1 || _month > 12)return false;//天数要合理// getMonthDay 函数后续实现if (_day < 1 || _day > getMonthDay())return false;return true;
}
📅判断闰年
闰年二月多一天,因此需要特殊处理
闰年判断技巧: 四年一闰且百年不闰 或者 四百年一闰
//闰年判断
bool Date::checkLeapYear() const
{//按照技巧判断if (((_year % 4 == 0) && (_year % 100 != 0)) || (_year % 400 == 0))return true;elsereturn false;
}
📅获取年份天数
闰年多一天,为 366
,非闰年为 365
,判断返回即可
//获取年份天数
int Date::getYearDay() const
{//复用代码return (checkLeapYear() ? 366 : 365);
}
📅获取月份天数
根据当前年份和月份,判断当月有多少天
注意: 闰年的二月需要特殊处理
//获取月份天数
int Date::getMonthDay() const
{//非闰年情况下每个月天数,其中下标代表月份int arr[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };//如果为2月,且为闰年,直接返回 2月+1天if (_month == 2 && checkLeapYear())return arr[_month] + 1;elsereturn arr[_month];
}
📆运算符重载
前面学习了 operator
运算符重载,现在正好可以拿来练练手
📅判断等于
两个日期相等的前提是 年
、月
、日
都相等
//运算符重载
//判断等于
bool Date::operator==(const Date& d) const
{return ((_year == d._year) && (_month == d._month) && (_day == d._day));
}
📅判断小于
注意: 我们的运算顺序都是 左操作数
、右操作数
,其中隐含的 this
指针默认为 左操作数
*this
小于 d
的逻辑
- 首选判断年是否小于
- 年相等,判断月是否小于
- 年相等,月相等,判断天是否小于
//判断小于
bool Date::operator<(const Date& d) const
{if (_year < d._year) //判断年return true;else if (_year == d._year && _month < d._month) //判断月return true;else if (_year == d._year && _month == d._month && _day < d._day) //判断天return true;elsereturn false;
}
📅复用至所有判断
善用代码复用,有了等于和小于,我们可以直接写出所有判断
//判断不等于
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) && !(*this == d));
}//判断大于等于
bool Date::operator>=(const Date& d) const
{//大于或等于return ((*this > d) || (*this == d));
}
📅重载流插入、提取
cout
、cin
只能输出、输出内置类型,但如果我们对它进行改造一下,就能直接输出我们的自定义类型
注意:
cout
类型为ostream
,cin
类型为istream
- 要使得
cout
、cin
变为重载后的左操作数,此时的运算符重载就不能写在类内,因为在类中的函数默认this
为第一个参数,即左操作数 - 因此这两个函数比较特殊,需要写在外面,但同时又得访问类中的成员,此时就需要
友元函数
- 两个函数都有返回值,返回的就是
cout
、cin
本身,避免出现cout << d1 << d2
这种情况
此时可以利用合法性检验了
实现 operator>> 时,右操作数不能用 const 修饰
//在 Date.h 内
//新增几个局部展开
using std::ostream;
using std::istream;
using std::endl;namespace Yohifo
{class Date{//声明为类的友元函数friend std::ostream& operator<<(std::ostream& out, const Date& d2);friend std::istream& operator>>(std::istream& in, Date& d2); //注意//……};//直接定义在头文件中,成为内联函数inline ostream& operator<<(ostream& out, const Date& d){//此时需要检验日期合法性if (Date(d).check() == false){out << "警告,当前日期非法!" << endl;out << "后续操作将会受到限制" << endl;}out << d._year << "年" << d._month << "月" << d._day << "日" << endl;return out;}inline istream& operator>>(istream& in, Date& d){Date tmp;flag:cout << "请入日期,格式为:年 月 日" << endl;in >> tmp._year >> tmp._month >> tmp._day;//如果输入日期非法,就重新输入if (Date(tmp).check() == false){cout << "警告,当前日期非法!" << endl;cout << "日期输入失败,请尝试重新输入!" << endl;goto flag;}cout << "输入成功!" << endl;return in;}
}
有了这两个运算符重载后,我们就可以直接对自定义类型(日期类对象)直接进行输入输出操作了
Date d1;
cin >> d1; //对自定义类型的输入
cout << d1; //对自定义类型的输出
📆日期+=天数
下面涉及两个重要算法
- 日期 += 天数
- 日期 -= 天数
这里把 日期 += 天数
介绍清楚了,日期 -= 天数
就很好写了,就是倒着走
- 有了
日期 += 天数
后,可以直接实现日期 + 天数
- 同理也可以实现
日期 - 天数
📅核心思想
注:此时实现的是 日期+=天数
进位思想:天数满了后进位到月份上,月份满后进位至年份上
注意:
- 每个月对应天数都需要计算,因为每个月都不同
- 月份为12月时,再+就变成了下一年的一月
- 假设为公元前,加至0年时,需要特殊处理为公元1年
+=
操作返回的是左操作数本身,应对(d1 += 10) = 20
这种情况
📅代码实现
//日期+=天数
Date& Date::operator+=(const int val)
{if (check() == false){cout << "警告,当前日期非法,无法进行操作" << endl;return *this;}//判断 val,避免恶意操作,如 d1 += -100if (val < 0){//此时需要调用 -=*this -= (-val);return *this;}//因为是 += 不需要创建临时对象//首先把天数全部加至 _day 上_day += val;//获取当前月份天数int monthDay = getMonthDay();//判断进位,直至 _day <= monthDaywhile (_day > monthDay){//此时大于,先把多余的天数减掉_day -= monthDay;//此时进位一个月++_month;//判断月份是否大于 12if (_month > 12){//此时需要进年++_year;//月份变为1月_month = 1;//判断是否为0年if (_year == 0)_year = 1; //调整}//重新获取月份天数monthDay = getMonthDay();}//返回 *this 本身return *this;
}
有了这个函数后,我们就可以根据当前日期推算 N 天后的日期
日期+天数
可以直接复用上面的代码,而日期-=天数
将逻辑反过来就行了,这里不展示代码了,完整代码在文末的 gitee 仓库中
📆日期-日期
日期+日期无意义,但日期-日期有,可以计算两日期差值
日期相减有两种情况:
左操作数
小于右操作数
,此时返回大于0的值左操作数
大于右操作数
,此时返回小于0的值
具体实现时也很好处理,直接用一个 flag
就行了
📅核心思想
先不管左右操作数大小,我们先找出较大操作数与较小操作数
通过较小操作数逐渐逼近较大操作数,其中经过的天数就是差值
步骤:
- 先把日期对齐,即小操作数日期与大操作数日期平齐
- 再把月份对齐
- 最后再把年份对齐就行了
- 随着步骤的深入,天数计算会越来越快的
除了这种方法外,我们还可以直接一天一天的加,直到相等,当然这种效率较低
📅代码实现
//日期 - 日期
const int Date::operator-(const Date& d) const
{if (check() == false || d.check() == false){cout << "警告,当前日期非法,无法进行操作!默认返回 0" << endl;return 0;}//假设右操作数为较大值Date max(d);Date min(*this);int flag = 1;//判断if (min > max){max = *this;min = d;flag = -1;}//小的向大的靠近int daySum = 0;//考虑天while (min._day != max._day){min += 1;daySum++;}//考虑月while (min._month != max._month){daySum += min.getMonthDay();min += min.getMonthDay();}//考虑年while (min._year != max._year){daySum += min.getYearDay();min._year++;}return daySum * flag;
}
这种方法(同轴转动)将会带来一定的性能提升(相对逐天相加来说)
方法 | 相差 1k 年 | 相差 1w 年 | 相差 10w 年 |
---|---|---|---|
同轴转动 | 耗时 0 ms | 耗时 0 ms | 耗时 2 ms |
逐天相加 | 耗时 28 ms | 耗时 297 ms | 耗时 3142 ms |
注:实际差异与电脑性能有关
📆自加、自减操作
自加操作实现很简单,不过需要注意编译器是如何区分两者的
占位参数
- 因为前置与后置的运算符重载函数名一致,此时需要给运算符多加一个参数以区分,这是由编译器规定的合法行为,
占位参数
加在后置运算符重载中
📅前置
前置直接复用前面 +=
的代码
前置操作是先进行自加或自减,再返回
//前置++
Date& Date::operator++()
{//直接复用*this += 1;return *this;
}//前置--
Date& Date::operator--()
{*this -= 1;return *this;
}
📅后置
此时需要借助 占位参数
,当启用时,编译器会自动传参,并自动区分,占位参数
类型为 int
后置操作是先记录值,再进行自加或自减,返回之前记录的值
//后置++
const Date Date::operator++(int)
{//借助临时变量Date tmp(*this);*this += 1;return tmp;
}//后置--
const Date Date::operator--(int)
{Date tmp(*this);*this -= 1;return tmp;
}
特别注意: 对于自定义类型来说,在进行自加、自减操作时,最好采用前置
,因为后置会发生拷贝构造
行为,造成资源浪费
📆程序源码
完整的代码在这里 Gitee
🗓️总结
以上就是关于日期类实现的全部内容了,涉及到了前面学的大部分知识,希望大家在看完后能把它独立敲一遍,加深理解
如果你觉得本文写的还不错的话,可以留下一个小小的赞👍,你的支持是我分享的最大动力!
如果本文有不足或错误的地方,随时欢迎指出,我会在第一时间改正
…
相关文章推荐
类和对象合集系列
类和对象(下)
类和对象(上)
类和对象(中)=============== C++入门必备
C++入门基础
![]()
相关文章:

类和对象实操之【日期类】
✨个人主页: Yohifo 🎉所属专栏: C修行之路 🎊每篇一句: 图片来源 The pessimist complains about the wind; the optimist expects it to change; the realist adjusts the sails. 悲观主义者抱怨风;乐观主义者期望它…...

微搭中如何实现弹性布局
我们在实际开发中经常可能会有一些社交的场景,比如开发一个类似朋友圈九宫格图片展示的功能。因为图片的数量不确定,所以需要实现图片的从左到右顺序排列。 在微搭中可以以可视化的方式设置样式。但是对于我们这类特殊需求,只用可视化设置显…...

九龙证券|外资强势出手!这只科创板百元股,被疯狂加仓
本周,北上资金净买入29.32亿元,连续第13周加仓A股。分商场看,北上资金加仓重点倾向于沪市的白马蓝筹股,沪股通取得50.34亿元,深股通则被净卖出21.02亿元。 食品饮料本周取得逾23亿元的增持,居职业首位&…...
51单片机最强模块化封装(4)
文章目录 前言一、创建key文件,添加key文件路径二、key文件编写三、模块化测试总结前言 本篇文章将为大家带来按键的模块化封装,这里使用到了三行按键使得我们的代码更加简便。 按键原理:独立按键 一、创建key文件,添加key文件路径 这里的操作就不过多解释了,大家自行看…...

五、Git本地仓库基本操作——分支管理
1. 什么是分支? master分支 我们在初始化git仓库的时候,会默认创建一个master分支,HEAD指针这时就会默认执行master分支。当我们在master分支提交(commit)了更新之后,master分支就会指向当前当前最新的co…...

vscode搭建python Django网站开发环境
这里使用pip安装的方式,打开命令行,输入执行: pip install django2.2这里选择安装2.2版本是因为是新的lts版本,长期支持稳定版。 接下来再安装pillow,Django底层一部分是基于pillow进行的。 pip install pillowpylint…...

【mybatis】实现分页查询
一 .使用原生分页器的实体类 1.1 java代码部分 方法多 不易书写 package cn.bdqn.entity;public class Page {private Integer pageIndex;//页码private Integer pageSize;//页大小 显示多少行数据private Integer totalCounts;//数据的总行数private Integer totalPages;//…...
CF1560D Make a Power of Two 题解
CF1560D Make a Power of Two 题解题目链接字面描述题面翻译题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1提示思路代码实现备注题目 链接 https://www.luogu.com.cn/problem/CF1560D 字面描述 题面翻译 给定一个整数 nnn。每次操作你可以做两件事情中的一件&am…...
C#开发的OpenRA的读取文件的函数
C#开发的OpenRA的读取文件的函数 在OpenRA游戏里,读取文件是必备的功能。 因为游戏大部分文件都是图片、动画、语音。 很久以前,我以为开发游戏的主要功能是在程序开发上, 其实游戏的大部分工作都不是在开发上,而是在美工方面。 因为游戏跟电影是一样,就是不断地展示场景,…...

SpringBoot结合XXL-JOB实现定时任务
Quartz的不足 Quartz 的不足:Quartz 作为开源任务调度中的佼佼者,是任务调度的首选。但是在集群环境中,Quartz采用API的方式对任务进行管理,这样存在以下问题: 通过调用API的方式操作任务,不人性化。需要…...

【Node.js】 创建web服务器
Node.js什么是客户端,什么是服务器服务器和普通电脑的区别什么是http模块导入http模块服务器相关概念创建web服务器的基本步骤req请求对象req响应对象解决中文乱码根据不同的url响应不同的html内容什么是客户端,什么是服务器 客户端在网络节点中&#x…...
基于go语言实现RestFul交互
一、RestFul 1.1 RestFul的介绍 RESTFUL(Representational State Transfer)是一种网络应用程序的设计风格和开发方式,基于HTTP或HTTPS,可以使用XML格式定义或JSON格式定义。RESTFUL适用于移动互联网厂商作为业务接口的场景&…...
情感溢出:读《浣溪沙》
浣溪沙 谁念西风独自凉 作者 纳兰性德 谁念西风独自凉,萧萧黄叶闭疏窗,沉思往事立残阳。 被酒莫惊春睡重,赌书消得泼茶香,当时只道是寻常。 记得年轻时学这篇词,就是愣背,现在也就记得这句当时只道是寻常…...

深入解读.NET MAUI音乐播放器项目(一):概述与架构
系列文章将分步解读音乐播放器核心业务及代码: 深入解读.NET MAUI音乐播放器项目(一):概述与架构深入解读.NET MAUI音乐播放器项目(二):播放内核深入解读.NET MAUI音乐播放器项目(三…...

【Python小游戏】某程序员将套圈游戏玩儿到了巅峰,好嗨哟~Pygame代码版《牛牛套圈》已上线,大人的套圈游戏太嗨了,小孩勿进。
前言 世上选择那么多。 关注栗子同学会是您最明智的选择哦。 所有文章完整的素材源码都在👇👇 粉丝白嫖源码福利,请移步至CSDN社区或文末公众hao即可免费。 “幸运牛牛套圈圈”套住欢乐,圈住幸福,等你来挑战…...
php的declare命令如何使用?
php中的declare结构用来设定一段代码的执行指令declare用于执行3个指令:ticks,encoding,strict_typesdeclare结构用于全局范围,影响到其后的所有代码(但如果有declare结构的文件被其他文件包含,则对包含他的父文件不起作用&#x…...

嵌软工程师要掌握的硬件知识2:一文看懂什么开漏和推挽电路(open-drain / push-pull)
想了解开漏和推挽,就要先了解一下三极管和场效应管是什么,在其他章节有详细介绍,本文就不再进行赘述。 1 推挽(push pull)电路 1.1 理解什么是推挽电路 - 详细介绍 如图所示,Q3是个NPN型三极管,Q4是个PNP型三极管。 1)当Vin电压为正时,上面的N型三极管控制端有电…...

1.2.6存储结构-磁盘管理:单缓冲区与双缓冲区读取、流水线周期、计算流水线执行时间
1.2.6存储结构-磁盘管理:单缓冲区与双缓冲区读取、流水线周期、计算流水线执行时间流水线周期计算流水线执行时间微秒,时间单位,符号μs(英语:microsecond ),1微秒等于百万分之一秒(…...
【pytest接口自动化测试】结合单元测试框架pytest+数据驱动模型+allure
api: 存储测试接口 conftest.py :设置前置操作 目前前置操作:1、获取token并传入headers,2、获取命令行参数给到环境变量,指定运行环境commmon:存储封装的公共方法 connect_mysql.py:连接数据库http_requests.py: 封装…...
展锐平台WIFI吞吐问题解决方案
同学,别退出呀,我可是全网最牛逼的 WIFI/BT/GPS/NFC分析博主,我写了上百篇文章,请点击下面了解本专栏,进入本博主主页看看再走呗,一定不会让你后悔的,记得一定要去看主页置顶文章哦。 一、Wi-Fi 吞吐验收标准 预置条件:屏蔽房;DUT 距离 AP 1m 左右;测试 AP 不加密;…...
React 第五十五节 Router 中 useAsyncError的使用详解
前言 useAsyncError 是 React Router v6.4 引入的一个钩子,用于处理异步操作(如数据加载)中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误:捕获在 loader 或 action 中发生的异步错误替…...

51c自动驾驶~合集58
我自己的原文哦~ https://blog.51cto.com/whaosoft/13967107 #CCA-Attention 全局池化局部保留,CCA-Attention为LLM长文本建模带来突破性进展 琶洲实验室、华南理工大学联合推出关键上下文感知注意力机制(CCA-Attention),…...

AI Agent与Agentic AI:原理、应用、挑战与未来展望
文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例:使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例:使用OpenAI GPT-3进…...

【项目实战】通过多模态+LangGraph实现PPT生成助手
PPT自动生成系统 基于LangGraph的PPT自动生成系统,可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析:自动解析Markdown文档结构PPT模板分析:分析PPT模板的布局和风格智能布局决策:匹配内容与合适的PPT布局自动…...
Nginx server_name 配置说明
Nginx 是一个高性能的反向代理和负载均衡服务器,其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机(Virtual Host)。 1. 简介 Nginx 使用 server_name 指令来确定…...
Spring Boot面试题精选汇总
🤟致敬读者 🟩感谢阅读🟦笑口常开🟪生日快乐⬛早点睡觉 📘博主相关 🟧博主信息🟨博客首页🟫专栏推荐🟥活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...
C# SqlSugar:依赖注入与仓储模式实践
C# SqlSugar:依赖注入与仓储模式实践 在 C# 的应用开发中,数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护,许多开发者会选择成熟的 ORM(对象关系映射)框架,SqlSugar 就是其中备受…...
数据库分批入库
今天在工作中,遇到一个问题,就是分批查询的时候,由于批次过大导致出现了一些问题,一下是问题描述和解决方案: 示例: // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...
大数据学习(132)-HIve数据分析
🍋🍋大数据学习🍋🍋 🔥系列专栏: 👑哲学语录: 用力所能及,改变世界。 💖如果觉得博主的文章还不错的话,请点赞👍收藏⭐️留言Ǵ…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...