当前位置: 首页 > news >正文

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.初始化(构造函数&#xff09;3.对日期进行天数推算4.比较相关的运算符重载5.前置后置自增或自减6.日期相减与流插入流提取1.日期相减2.重载流插入和流提取3.总结前言 之前介绍了C…...

想成为一名专业黑客,但不知道从哪里学起?我来教你。

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

VMware ESXi 7.0 U3k Unlocker OEM BIOS 集成网卡驱动和 NVMe 驱动 (集成驱动版)

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

新的计算方法:预测益生菌在不同生长条件下的相互作用

谷禾健康 益生菌可以产生有益的维生素、消化酶、必需氨基酸、免疫调节和抗菌代谢产物&#xff0c;从而促进人体健康&#xff0c;预防肠道炎症性疾病、自身免疫性疾病和胃肠道感染。其宝贵特性已得到健康行业、医疗专业人士和公众的认可。 比起单菌株益生菌&#xff0c;多菌株益…...

python自学之《21天学通Python》(13)——第16章 数据库编程

数据库指的是以一定方式存储在一起、能为多个用户共享、具有尽可能小的冗余度、与应用程序彼此独立的数据集合。而我们平时所说的数据库实际上是包含了数据库管理系统&#xff08;DBMS&#xff09;的&#xff0c;数据库管理系统是为管理数据库而设计的软件系统&#xff0c;它一…...

[架构之路-118]-《软考-系统架构设计师》-软架构设计-11-可靠性相关设计

第11节 可靠性相关设计11.1 可靠性基本概念可靠性工程是研究产品生命周期中故障的发生、发展规律&#xff0c;达到预防故障&#xff0c;消灭故障&#xff0c;提高产品可用性的工程技术。信息系统的可靠性是指系统在满足一定条件的应用环境中能够正常工作的能力&#xff0c;可以…...

电阻串联的作用

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

leetcode 1675. Minimize Deviation in Array(最小化数组偏差)

数组里面有n个正整数&#xff0c;里面的数字可以无限次进行如下操作&#xff1a; 1.偶数可以除以2 2.奇数可以乘以2 数组中任意两元素差的最大值称为偏差。 把数组中的元素进行上面2种操作&#xff0c;使偏差最小。 思路&#xff1a; 数组中现有2种数字&#xff0c;一种是奇数…...

特征向量中心度(eigenvector centrality)算法原理与源码解析

前言 随着图谱应用的普及&#xff0c;图深度学习技术也逐渐被越来越多的数据挖掘团队所青睐。传统机器学习主要是对独立同分布个体的统计学习&#xff0c;而图深度学习则是在此基础上扩展到了非欧式空间的图数据之上&#xff0c;通过借鉴NLP和CV方向的模型思想&#xff0c;衍生…...

Vue3 中组件的使用(上)

目录前言&#xff1a;一、什么是组件二、注册组件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&#xff0c;不需要花费太多的时间&#xff0c;轻松从2D平面图生成3D楼梯模型&#xff0c;生成的楼梯模型细节丰富真实。 【主要功能】 1.简单&#xff1a;轻松实现2D到3D建模。 2.具有最详细三维结构的台阶平面图。 3.楼梯各部件完全参数化…...

OpenShift 简介

OpenShift 是红帽 Red Hat 公司基于开源的云平台&#xff0c;是平台即服务&#xff08;PaaS&#xff09;&#xff0c;是一种容器应用平台。允许开发人员构建、测试和部署云应用。该系统是在 K8S 核心之上添加工具&#xff0c;从而实现更快的应用开发、部署及扩展。 在 OpenShi…...

netty自定义封包实现

文章目录说明分享内置编码器和解码器解码器编码器代码实现创建核心类消息实体类自定义编码类自定义解码类服务端ServerHandler入口类客户端ClientHandler入口类测试参考总结说明 netty是java重要的企业级NIO&#xff0c;使用它可以快速实现很多功能通信功能如&#xff1a;http、…...

ORA error集锦

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

格雷码的实现

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

快到金3银4了,准备跳槽的可以看看

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

最新BlackArch发布,提供1400款渗透测试工具

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

重走前端路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是一门多范式的编程语言&#xff0c;它同时支持过程式、面向对象和函数式的编程范式。因此&#xff0c;在Python中提供了很多符合 函数式编程 风格的特性和工具。 二、lambda表达式&#xff08;匿名函数&#xff09; 除了 函数 中介绍的 def语句&#xff0c;P…...

【YBT2023寒假Day14 C】字符串题(SAM)(树链剖分)(线段树)

字符串题 题目链接&#xff1a;YBT2023寒假Day14 C 题目大意 对于一个字符串 S 定义 F(S) 是 fail 树上除了 0 点其它点的深度和。 G(S) 是 S 每个子串 S’ 的 F(S’) 之和。 然后一个空串&#xff0c;每次在后面加一个字符&#xff0c;要你维护这个串的 G 值。 思路 考虑…...

Tailwind CSS 在Vue中的使用

什么是Tailwind CSS&#xff1f; Tailwind CSS 是一个功能类优先的 CSS 框架&#xff0c;它集成了诸如 flex, pt-4, text-center 和 rotate-90 这样的的类&#xff0c;支持 hover 和 focus 样式&#xff0c;它们能直接在脚本标记语言中组合起来&#xff0c;构建出任何设计。 …...

三层楼100人办公网络如何规划设计实施(实战案例)

如何设计组网 1.采用防火墙+三层交换机+二层POE交换机+AP的方案 2.三层交换机作为网络的核心,提供网络的配置、划分和各个VLAN间的数据交换,而每个VLAN由二层交换机组建 3.网络主干设备的选型,建议网络主干设备或核心层设备选择具备第3层交换功能的高性能主干交换机。 4…...

Redis:实现全局唯一ID

Redis&#xff1a;实现全局唯一ID一. 概述二. 实现&#xff08;1&#xff09;获取初始时间戳&#xff08;2&#xff09;生成全局ID三. 测试为什么可以实现全局唯一&#xff1f;其他唯一ID策略补充&#xff1a;countDownLatch一. 概述 全局ID生成器&#xff1a;是一种在【分布式…...

webpack打包基本原理——实现webpack打包核心功能

webpack打包的基本原理 核心功能就是把我们写的模块化代码转换成浏览器能够识别运行的代码&#xff0c;话不多说我们一起来了解它 首先我们建一个空项目用 npm init -y 创建一个初始化的&#xff0c;在跟目录下创建src文件夹&#xff0c;src下创建index.js&#xff0c;add.js…...

git的使用(终端输入指令) 上

git目录前言1.创建仓库2.创建文件和修改数据状态分区![分区](https://img-blog.csdnimg.cn/d124dec6b2b14769ad20b75490f29cae.png)3 .删除、撤销重置 、和比较前言 今天带大家手把手敲一遍 git 流程&#xff1a; 安装一下git&#xff08;详细观看我之前发的git文档&#xff0…...

react定义css样式,使用less,css模块化

引入外部 css文件 import ./index.css此时引入的样式是全局样式 使用less 安装 npm i style-loader css-loader sass-loader node-sass -D生成config文件夹 npm run eject配置 以上代码运行完&#xff0c;会在根目录生成config文件夹 进入 config > webpack.config.js 查找…...

基于JavaWeb的学生管理系统

文章目录 项目介绍主要功能截图:登录用户信息管理院系信息管理班级信息管理新增学生课程管理成绩管理部分代码展示设计总结项目获取方式🍅 作者主页:Java韩立 🍅 简介:Java领域优质创作者🏆、 简历模板、学习资料、面试题库【关注我,都给你】 🍅文末获取源码联系�…...

win11右键新建菜单添加选项

需要操作 2 处注册表&#xff0c; 以下以在右键新建菜单中添加 .html 为例 在主键 HKEY_CLASSES_ROOT 中&#xff0c;搜索 .html 找到后 &#xff0c;右键点击它&#xff0c;选 新建 ->项&#xff0c; 在这里插入图片描述 项目名字是&#xff1a;ShellNew 新建后&#x…...

leetcode Day5(卡线复试,放弃版)

Day5 最后一个单词长度&#xff08;要求最后一个&#xff0c;可以反向计数&#xff09; int lens.length()-1; while(s.charAt(len)){len--;//最后是一个空格&#xff0c;就是无字符时 } int wordlen0;//记录字符长度 /*charAt() 方法用于返回指定索引处的字符。索引范围为从 0…...