C++实现日期类
文章目录
- 前言
- 1.日期类的功能分析
- 1.大致分析
- 2.接口设计
- 2.具体实现
- 1.日期类的成员函数和成员变量
- 2.初始化(构造函数)
- 3.对日期进行天数推算
- 4.比较相关的运算符重载
- 5.前置后置自增或自减
- 6.日期相减与流插入流提取
- 1.日期相减
- 2.重载流插入和流提取
- 3.总结
前言
之前介绍了C++面向对象的初阶知识,为了更理解相关知识,加深印象,我们可以先试着写个日期类来巩固所学。
1.日期类的功能分析
1.大致分析
我们写日期类之前可以先去百度看看一些简蛋的日期转化工具的大致功能,从中提炼出我们自己日期类的功能。我大致将日期类分为以下功能:天数转化日期(给定一日期往前推算日期或往后推算日期),两个日期减相差多少天,为了用到运算符重载的知识可以进行日期的判断比较,已经针对输入输出实现输入输出的运算符重载。
2.接口设计
日期类构造函数这里就不算在内了,同时日期类也不设计资源申请,这里就不提析构函数的实现了,这里主要是围绕日期类功能的相关接口。
打印函数 void Print();
得到月份的天数 int GetMonthDay(int year, int month);
日期+=天数 Date& operator+=(int day);
日期+天数 Date operator+(int day);
日期-天数 Date operator-(int day);
日期-=天数 Date& operator-=(int day);
后置++ Date operator++(int);
后置-- Date operator--(int);
前置++ Date& operator++();
前置-- Date& operator--();
赋值重载 Date& 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);
!=运算符重载 bool operator != (const Date& d);
日期-日期 返回天数 int operator-(const Date& d);
流插入 friend ostream& operator<< (ostream& out, const Date& d);
流提取friend istream& operator>>(istream& in, Date& d);
日期类的成员这都很好确定,大致就3个:year month day.
2.具体实现
1.日期类的成员函数和成员变量
class Date
{
public://打印函数void Print();//得到月份的天数int GetMonthDay(int year, int month);//全缺省构造函数Date(int year=1, int month=1, int day=1);//拷贝构造Date(const Date &d);// 析构函数~Date(){;}// 日期+=天数Date& operator+=(int day);// 日期+天数Date operator+(int day);// 日期-天数Date operator-(int day);// 日期-=天数Date& operator-=(int day);// 后置++Date operator++(int);// 后置--Date operator--(int);//前置++Date& operator++();// 前置--Date& operator--();//赋值重载Date& 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);// !=运算符重载bool operator != (const Date& d);// 日期-日期 返回天数int operator-(const Date& d);//流插入friend ostream& operator<< (ostream& out, const Date& d);//流提取friend istream& operator>>(istream& in, Date& d);private:int _year;int _month;int _day;
};
其实这个日期类成员全凭借个人意愿,有些功能如果没想好,可以边写边想,或者后序加上也行。不过最好还是写代码之前提前想好,这样就可以专心写代码实现功能了。
2.初始化(构造函数)
之前我们知道构造函数就是用来初始化的,这里我就实现了两个构造,一个直接构造,另一个是拷贝构造。这里要注意一点对于用户输入的日期要进行判断,不能使用非法的值用来构造。
其实这里的拷贝构造我们不现实也可,可以使用默认生成的,因为日期类不涉及一些资源的申请。
Date::Date(int year=1,int month=1,int day=1)
{if(month > 0 && month < 13&& (day > 0 && day<=GetMonthDay(year, month))){ _year = year;_month = month;_day = day;}else{cout << "非法初始化" << endl;}}Date::Date(const Date& d)
{_year = d._year;_month = d._month;_day = d._day;
}
我们可以先写个Getmonthday函数用来获取每一年每个月的天数,这里要注意对闰年的判断。
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 && ((year % 4 == 0 && year % 100 != 0) || (year % 400) == 0)){return 29;}else{return MonthDay[month];}
}
这里只用对2月进行单独处理即可。当然也可以加入一些对输入年份和月份判断限制。我这里就没有那么严谨进行限制判断。
3.对日期进行天数推算
这里就需要用到了运算符重载的知识了,天数推算就是日期类对象进行天数的相加或者相减。为了支持这种相减相加的操作,只能对+ -运算符进行重载了。
关于日期天数加键的逻辑分析
+= 与-=
Date& Date::operator+=(int day)
{ //复用-=运算符重载if (day < 0){*this -= -day;}_day += day;while (_day > GetMonthDay(_year, _month)){_day -= GetMonthDay(_year, _month);_month++;if (_month == 13){_month = 1;_year++;}}return *this;
}Date& Date:: operator-=(int day)
{ //复用+=运算符重载if (day < 0){*this +=-day;}_day -= day;while (_day <= 0){_month--;if (_month == 0){_month = 12;_year--;}_day += GetMonthDay(_year, _month);}return *this;
}
这里使用引用返回是为了更加符合运算符特性,比如:b=(a+=c),如果没有返回值的话,出现例子种的场景显然是有问题的。代码中还涉及复用的技巧,对于为负值的天数相加时,可以复用相减的运算符重载,就是减去负day。处理相减时也可以这样处理,如果day是负数,就是加上负day。
+ 与-
Date Date::operator+(int day)
{ //复用+= //tem是临时变量所以不用引用返回Date tem = *this;tem += day;return tem;
}Date Date::operator-(int day)
{Date tem = *this;tem += day;return tem;
}
这里要提一点 a+=1 a的值肯定是改变了, a+1 这个a的值肯定是没有改变的。基于此实现- +运算符重载的时候,先创建一个临时变量,为了不该变原来对象中的数据。因为是在函数体内创建的临时变量实用这里就使用的传值返回。当然这个返回值的也是为了符合运算符的特性:比如a=b-1
因为之前实现了+=和-=这里对临时变量直接复用即可。
这里补充一个运算符重载=赋值运算符重载
Date& Date::operator=(const Date& d)
{ if (this != &d){_year = d._year;_month = d._month;_day = d._day;}return *this;
}
这里实现的时候多加了一个判断自己对自己赋值的时候不用处理,减少不必要的程序系统开销。
为了支持运算符连续赋值的特性,这里采用的也是引用返回。
4.比较相关的运算符重载
之前我们实现- +运算符重载的时候使用了代码复用,这里也可以代码复用,我们可以先实现两个运算符重载,其他的都可以复用,我选取的<和==,其他都是复用这两个。
bool Date::operator==(const Date& d)
{return _year == d._year && _month == d._month && _day == d._day;
}bool Date::operator<(const Date& d)
{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;}else{return false;}
}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 || *this == d;
}bool Date::operator!=(const Date& d)
{return !(*this == d);
}
当我们使用了<和==之后,如果两个日期类对象,是小于或者等于就实现了<=,如果不是<=就是大于,如果大于或者等于就实现了>=,如果不是 == 就是!=,这样一来代码的复用就实现了。代码复用算是一个编程小技巧,尽可能的多用。
5.前置后置自增或自减
前置++ 我们知道改变自己,后置++不改变自己。同时为了保持原本运算符的连续性,运算符重载是需要有返回值的,这点上面就提到过,返回值类型根据运算符特性特性来决定,如果是改变自己采用引用返回即可,其他情况就采用采用传值返回。
前置++ 代码示例
Date& Date::operator++()
{//复用*this += 1;return *this;
}
后置++ 代码示例
//后置++
Date Date::operator++(int)
{Date tmp(*this);tmp+= 1;return tmp;
}
这里要提一下,C++中为了区分前置++和后置++ ,后置++多了一个int 参数 主要是为了构成函数重载,用于区分这两个运算符重载,实际使用的时候不用传参。同理,后置--的时候也需要这样处理。
前置-- 代码示例
//前置--
Date& Date:: operator--()
{*this -= 1;return *this;
}
后置-- 代码示例
//后置--
Date Date::operator--(int)
{Date tem(*this);tem -= 1;return tem;
}
当然这里实现的时候也是代码复用
6.日期相减与流插入流提取
1.日期相减
日期相减我们可以引入一个计数变量用于计算两个日期的差值,我们先选出较小的日期进行前置++直到和较大的日期相等为止,每自增一次就计数一次。这样就可以很好的计算出日期差值。
代码示例
int Date::operator-(const Date& d)
{Date max(*this);Date min(d);int flag = 1;if (max < min){max = d;min = *this;flag = -1;}//复用!=int day = 0;while (max != min){++min;++day;}return flag * day;
}
可能前者的日期比后者的大,所以引入一个变量flag标记一下,用于确定结果的正负值。这里还是涉及到代码的复用。
2.重载流插入和流提取
我们刚接触c++的时候可能觉得cout和cin很神奇能够自动识别数据类型,
通过之前的学习我们不难猜出cont和cin是使用了运算符重载和函数重载能够自动识别数据类型。我们可以去c++官网查一下cout和cin.
我们不难看出cin 是iostrem头文件中的一个对象,它的类型是istream,cout也是iostrem头文件中的一个对象,它的类型是ostream。
因为c++的官网库中对内置类型进行函数重载和运算符重载了,但是没有对日期类进行重载,我们日期类无法直接cout << Date 或者cin>> Date ,我们不能动官方库,只能直接对是>>进行重载,如果将其封装成Date类的成员函数就会发生如下问题:
这样写用倒是能用 但是过于奇怪了。为了更加和库中的使用方法保持一致,
我们将这个函数写成全局函数,写成全局函数后就有个问题不能访问类中的成员,为了能够访问类中成员我们必须得使用friend将这个全局函数设置成类中的友元函数来处理。
同理,cin也是这样处理。
//为了能沟连续输出或输入采用引用作为返回值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;}
3.总结
日期类实现难点在于理清加减天数的逻辑,这点是比较重要的。理清后后面的代码就很好写了。为了得到每个月的天数这个功能是频繁使用的,我们可以将其封装成一个函数,这样代码逻辑更加清晰。
编程技巧:
学会代码的复用,这样可以使得代码更加简洁,同时对于一些不用改变成员变量的函数形参可以使用const修饰,尽可能使用const是个好习惯。比如上述的日期比较函数不仅可以使用const修饰还可以让其成为const成员函数,专门用于修饰*this。因为这种只需要读权限即可,const 对象也可以调用该成员函数。
以上内容如有问题,欢迎指正,谢谢!
相关文章:

C++实现日期类
文章目录前言1.日期类的功能分析1.大致分析2.接口设计2.具体实现1.日期类的成员函数和成员变量2.初始化(构造函数)3.对日期进行天数推算4.比较相关的运算符重载5.前置后置自增或自减6.日期相减与流插入流提取1.日期相减2.重载流插入和流提取3.总结前言 之前介绍了C…...

想成为一名专业黑客,但不知道从哪里学起?我来教你。
成为一名黑客需要学什么? 想成为一名专业黑客,但不知道从哪里学起”很多人在后台问过这个问题,今天就为你介绍成为专业黑客必须学习的十个方面的知识,希望能为迷惘中的你指明方向。 想要成为网络hacker黑客?先来学习…...

VMware ESXi 7.0 U3k Unlocker OEM BIOS 集成网卡驱动和 NVMe 驱动 (集成驱动版)
ESXi 7 U3 标准版集成 Intel 网卡、USB 网卡 和 NVMe 驱动 请访问原文链接:https://sysin.org/blog/vmware-esxi-7-u3-sysin/,查看最新版。原创作品,转载请保留出处。 作者主页:www.sysin.org 本次针对 2023-02-21 发布的 ESXi …...

新的计算方法:预测益生菌在不同生长条件下的相互作用
谷禾健康 益生菌可以产生有益的维生素、消化酶、必需氨基酸、免疫调节和抗菌代谢产物,从而促进人体健康,预防肠道炎症性疾病、自身免疫性疾病和胃肠道感染。其宝贵特性已得到健康行业、医疗专业人士和公众的认可。 比起单菌株益生菌,多菌株益…...

python自学之《21天学通Python》(13)——第16章 数据库编程
数据库指的是以一定方式存储在一起、能为多个用户共享、具有尽可能小的冗余度、与应用程序彼此独立的数据集合。而我们平时所说的数据库实际上是包含了数据库管理系统(DBMS)的,数据库管理系统是为管理数据库而设计的软件系统,它一…...

[架构之路-118]-《软考-系统架构设计师》-软架构设计-11-可靠性相关设计
第11节 可靠性相关设计11.1 可靠性基本概念可靠性工程是研究产品生命周期中故障的发生、发展规律,达到预防故障,消灭故障,提高产品可用性的工程技术。信息系统的可靠性是指系统在满足一定条件的应用环境中能够正常工作的能力,可以…...

电阻串联的作用
电阻串联常见作用 第一个作用是:阻抗匹配: 因为信号源的阻抗很低,跟信号线之间阻抗不匹配,串上一个电阻后,可以改善匹配情况,以减少反射,避免振荡等。 常见的阻抗匹配方法 1、使用变压器来做…...

leetcode 1675. Minimize Deviation in Array(最小化数组偏差)
数组里面有n个正整数,里面的数字可以无限次进行如下操作: 1.偶数可以除以2 2.奇数可以乘以2 数组中任意两元素差的最大值称为偏差。 把数组中的元素进行上面2种操作,使偏差最小。 思路: 数组中现有2种数字,一种是奇数…...

特征向量中心度(eigenvector centrality)算法原理与源码解析
前言 随着图谱应用的普及,图深度学习技术也逐渐被越来越多的数据挖掘团队所青睐。传统机器学习主要是对独立同分布个体的统计学习,而图深度学习则是在此基础上扩展到了非欧式空间的图数据之上,通过借鉴NLP和CV方向的模型思想,衍生…...

Vue3 中组件的使用(上)
目录前言:一、什么是组件二、注册组件1. 全局注册2. 局部注册二、传递数据【父 -> 子】1. 字符串数组的形式2. 对象的形式三、组件事件【子 -> 父】1. 字符串数组式声明自定义事件2. 【子组件】触发组件事件3. 【父组件】监听子组件自定义事件4. 组件事件例子…...

spring-boot、spring-cloud、spring-cloud-alibaba版本对应
一、查询 spring-boot(spring-boot-starter-parent) 版本号 https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-parent 二、查询 spring-cloud(spring-cloud-dependencies) 版本号 https://mvnrepository.com/artifact/org.springframework…...

【沐风老师】3DMAX一键楼梯脚本插件StairGenerator使用教程
3DMAX一键楼梯插件StairGenerator,不需要花费太多的时间,轻松从2D平面图生成3D楼梯模型,生成的楼梯模型细节丰富真实。 【主要功能】 1.简单:轻松实现2D到3D建模。 2.具有最详细三维结构的台阶平面图。 3.楼梯各部件完全参数化…...

OpenShift 简介
OpenShift 是红帽 Red Hat 公司基于开源的云平台,是平台即服务(PaaS),是一种容器应用平台。允许开发人员构建、测试和部署云应用。该系统是在 K8S 核心之上添加工具,从而实现更快的应用开发、部署及扩展。 在 OpenShi…...
netty自定义封包实现
文章目录说明分享内置编码器和解码器解码器编码器代码实现创建核心类消息实体类自定义编码类自定义解码类服务端ServerHandler入口类客户端ClientHandler入口类测试参考总结说明 netty是java重要的企业级NIO,使用它可以快速实现很多功能通信功能如:http、…...

ORA error集锦
1、oralce 数据客户端需要安装的问题 保存信息为: “无法连接到数据库,因为数据库客户端软件无法加载。确保已正确安装并配置数据库客户端软件” 从百度网盘下载,并安装win32 oracle client 安装包 2、ORA错误 “执行异常,ORA-00911: inval…...

格雷码的实现
格雷码:任意两个相邻的二进制数之间只有一位不同 想必通信专业的学生应该都接触过格雷码,它出现在数电、通信原理等课程里。 如下图所示一个四位格雷码是什么样子的: 格雷码的特点: 其最大的特点是任意上下相邻的两个码值间&am…...

快到金3银4了,准备跳槽的可以看看
前两天跟朋友感慨,今年的铜九铁十、裁员、疫情导致好多人都没拿到offer!现在已经12月了,具体明年的金三银四只剩下两个月。 对于想跳槽的职场人来说,绝对要从现在开始做准备了。这时候,很多高薪技术岗、管理岗的缺口和市场需求也…...

最新BlackArch发布,提供1400款渗透测试工具
近日,BlackArch Linux新版本发布,此版本为白帽子和安全研究人员提供了大约1400款渗透测试工具,如果你是一位白帽子或者安全研究人员,这个消息无疑会让你很感兴趣。BlackArch Linux是一款基于Arch Linux的发行版,主要面…...

重走前端路JS进阶篇:This 指向与箭头函数
JavaScript 高级 This 指向规则 案例 function foo() {console.log(this)}// 1 调用方式1foo();// 2 调用方式2 放入对象中调用var obj {name: "why",foo: foo}obj.foo()// 调用方式三 通过 call/apply 调用foo.call("abc")指向定义 this 是js 给函数的…...
Python基础:函数式编程
一、概述 Python是一门多范式的编程语言,它同时支持过程式、面向对象和函数式的编程范式。因此,在Python中提供了很多符合 函数式编程 风格的特性和工具。 二、lambda表达式(匿名函数) 除了 函数 中介绍的 def语句,P…...

wordpress后台更新后 前端没变化的解决方法
使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…...
Leetcode 3576. Transform Array to All Equal Elements
Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接:3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到…...
在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能
下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能,包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...
是否存在路径(FIFOBB算法)
题目描述 一个具有 n 个顶点e条边的无向图,该图顶点的编号依次为0到n-1且不存在顶点与自身相连的边。请使用FIFOBB算法编写程序,确定是否存在从顶点 source到顶点 destination的路径。 输入 第一行两个整数,分别表示n 和 e 的值(1…...

人机融合智能 | “人智交互”跨学科新领域
本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...

招商蛇口 | 执笔CID,启幕低密生活新境
作为中国城市生长的力量,招商蛇口以“美好生活承载者”为使命,深耕全球111座城市,以央企担当匠造时代理想人居。从深圳湾的开拓基因到西安高新CID的战略落子,招商蛇口始终与城市发展同频共振,以建筑诠释对土地与生活的…...

vulnyx Blogger writeup
信息收集 arp-scan nmap 获取userFlag 上web看看 一个默认的页面,gobuster扫一下目录 可以看到扫出的目录中得到了一个有价值的目录/wordpress,说明目标所使用的cms是wordpress,访问http://192.168.43.213/wordpress/然后查看源码能看到 这…...
LRU 缓存机制详解与实现(Java版) + 力扣解决
📌 LRU 缓存机制详解与实现(Java版) 一、📖 问题背景 在日常开发中,我们经常会使用 缓存(Cache) 来提升性能。但由于内存有限,缓存不可能无限增长,于是需要策略决定&am…...
【深尚想】TPS54618CQRTERQ1汽车级同步降压转换器电源芯片全面解析
1. 元器件定义与技术特点 TPS54618CQRTERQ1 是德州仪器(TI)推出的一款 汽车级同步降压转换器(DC-DC开关稳压器),属于高性能电源管理芯片。核心特性包括: 输入电压范围:2.95V–6V,输…...
Docker、Wsl 打包迁移环境
电脑需要开启wsl2 可以使用wsl -v 查看当前的版本 wsl -v WSL 版本: 2.2.4.0 内核版本: 5.15.153.1-2 WSLg 版本: 1.0.61 MSRDC 版本: 1.2.5326 Direct3D 版本: 1.611.1-81528511 DXCore 版本: 10.0.2609…...