当前位置: 首页 > 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…...

微信小程序之bind和catch

这两个呢&#xff0c;都是绑定事件用的&#xff0c;具体使用有些小区别。 官方文档&#xff1a; 事件冒泡处理不同 bind&#xff1a;绑定的事件会向上冒泡&#xff0c;即触发当前组件的事件后&#xff0c;还会继续触发父组件的相同事件。例如&#xff0c;有一个子视图绑定了b…...

Zustand 状态管理库:极简而强大的解决方案

Zustand 是一个轻量级、快速和可扩展的状态管理库&#xff0c;特别适合 React 应用。它以简洁的 API 和高效的性能解决了 Redux 等状态管理方案中的繁琐问题。 核心优势对比 基本使用指南 1. 创建 Store // store.js import create from zustandconst useStore create((set)…...

工程地质软件市场:发展现状、趋势与策略建议

一、引言 在工程建设领域&#xff0c;准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具&#xff0c;正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...

ServerTrust 并非唯一

NSURLAuthenticationMethodServerTrust 只是 authenticationMethod 的冰山一角 要理解 NSURLAuthenticationMethodServerTrust, 首先要明白它只是 authenticationMethod 的选项之一, 并非唯一 1 先厘清概念 点说明authenticationMethodURLAuthenticationChallenge.protectionS…...

成都鼎讯硬核科技!雷达目标与干扰模拟器,以卓越性能制胜电磁频谱战

在现代战争中&#xff0c;电磁频谱已成为继陆、海、空、天之后的 “第五维战场”&#xff0c;雷达作为电磁频谱领域的关键装备&#xff0c;其干扰与抗干扰能力的较量&#xff0c;直接影响着战争的胜负走向。由成都鼎讯科技匠心打造的雷达目标与干扰模拟器&#xff0c;凭借数字射…...

laravel8+vue3.0+element-plus搭建方法

创建 laravel8 项目 composer create-project --prefer-dist laravel/laravel laravel8 8.* 安装 laravel/ui composer require laravel/ui 修改 package.json 文件 "devDependencies": {"vue/compiler-sfc": "^3.0.7","axios": …...

Redis的发布订阅模式与专业的 MQ(如 Kafka, RabbitMQ)相比,优缺点是什么?适用于哪些场景?

Redis 的发布订阅&#xff08;Pub/Sub&#xff09;模式与专业的 MQ&#xff08;Message Queue&#xff09;如 Kafka、RabbitMQ 进行比较&#xff0c;核心的权衡点在于&#xff1a;简单与速度 vs. 可靠与功能。 下面我们详细展开对比。 Redis Pub/Sub 的核心特点 它是一个发后…...

基于SpringBoot在线拍卖系统的设计和实现

摘 要 随着社会的发展&#xff0c;社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。 在线拍卖系统&#xff0c;主要的模块包括管理员&#xff1b;首页、个人中心、用户管理、商品类型管理、拍卖商品管理、历史竞拍管理、竞拍订单…...

解读《网络安全法》最新修订,把握网络安全新趋势

《网络安全法》自2017年施行以来&#xff0c;在维护网络空间安全方面发挥了重要作用。但随着网络环境的日益复杂&#xff0c;网络攻击、数据泄露等事件频发&#xff0c;现行法律已难以完全适应新的风险挑战。 2025年3月28日&#xff0c;国家网信办会同相关部门起草了《网络安全…...

【从零开始学习JVM | 第四篇】类加载器和双亲委派机制(高频面试题)

前言&#xff1a; 双亲委派机制对于面试这块来说非常重要&#xff0c;在实际开发中也是经常遇见需要打破双亲委派的需求&#xff0c;今天我们一起来探索一下什么是双亲委派机制&#xff0c;在此之前我们先介绍一下类的加载器。 目录 ​编辑 前言&#xff1a; 类加载器 1. …...