C++从入门到入土(四)--日期类的实现
目录
前言
日期类的实现
日期的获取
日期的比较
const成员函数
日期的加减
日期的加等
日期的减等
日期的加减
日期的加加减减
日期的相减
流插入和提取的重载
友元
友元的特点
日期类代码
总结
前言
前面我们介绍了C++中类和对象的相关知识和六个默认成员函数,在此基础上我们可以用C++实现一个日期类,这样可以帮助我们更加深入理解C++中的知识点,如果文章中有不懂的可以参考之前的文章
C++从入门到入土(三)--6个默认成员函数
C++从入门到入土(二)——初步认识类与对象
日期类的实现
我们在创建一个项目之前首先要知道这个项目要完成什么功能,以日期类为例,我们要实现日期的比较,日期的加减,以及日期的获取等功能,所以根据之前学的运算符重载等方面的知识我们可以很轻松地在Date.h文件中声明这个类所要实现的各种功能,如下所示
#pragma once
#include<iostream>
using namespace std;class Date
{friend ostream& operator<<(ostream& out, const Date& d);friend istream& operator>>(istream& in, Date& d);
public:Date(int year = 1900, int month = 1, int day = 1);int GetMonthDay(int year, int month){int GetMonthDayArry[13] = { 0,31,30,31,30,31,30,31,31,30,31,30,31 };if (2 == month && (year % 4 == 0 && year % 100 != 0 )|| year % 400 == 0){return 29;}else{return GetMonthDayArry[month];}}bool CheckDate();void Print()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;bool operator!=(const Date& d)const;Date& operator+=(int day);Date operator+(int day)const;Date& operator-=(int day);int operator-(const Date& d)const;Date& operator++();Date operator++(int);Date& operator--();Date operator--(int);
private:int _year;int _month;int _day;
};ostream& operator<<(ostream& out, const Date& d);
istream& operator>>(istream& in, Date& d);
下面我将逐一分析实现相关的功能。
日期的获取
我们创建好一个日期类后,首先要获取里面的日期,于是我们创建了一个GetMonthDay的成员函数,可以看到,它的返回值是int,在函数里面我们创建了一个名为GetMonthDayArry的数组,这个数组里面存放了每个月的天数,为了方便通过传月份来获取日期我们给这个数组的数据个数定为13,但是我们此时面临一个问题:遇到闰年的2月该怎么办呢?那么此时我们就要特殊情况特殊处理,闰年的判断规则是:四年一润,百年不润,四百年再润,于是我们便可以轻松写下如下代码:
int GetMonthDay(int year, int month)
{int GetMonthDayArry[13] = { 0,31,30,31,30,31,30,31,31,30,31,30,31 };if (2 == month && (year % 4 == 0 && year % 100 != 0 )|| year % 400 == 0){return 29;}else{return GetMonthDayArry[month];}
}
日期的比较
通过运算符重载的相关知识我们知道,作为类的成员函数重载时,有一个隐藏的this指针,因此我们只需要显示传一个参数,因此我们就有了以下定义:
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;
由于C++的语法,我们在实现上述逻辑时只需要实现其中的两个,其他的直接复用就可以轻松完成以上任务,我们在这里只实现小于和等于两个逻辑,其余直接复用即可,请参考以下代码:
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){if (_day < d._day){return true;}}}return false;
}
bool Date::operator<=(const Date& d)const
{return (*this == d || *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 _year == d._year && _month == d._month && _day == d._day;
}
bool Date::operator!=(const Date& d)const
{return !(*this == d);
}
const成员函数
我们发现,在以上几个日期的比较函数的后面我们都加上了const,这是为什么呢? 实际上我们将成员函数后面加const的函数称为const成员函数,从表面上看const是在修饰类的成员函数,实际上是在修饰该成员函数隐藏的this指针,表明在该成员函数中不能对类的任何成员进行修改,即:只要不修改成员的就可以在后面加上const
日期的加减
日期的加等
我们首先来介绍日期的加等,日期的加等原则就是进位法,即:将天数全加上去,如果超过该月的天数就将该月的天数减去,此时月数进一,如果超过12个月那么年数进一,于是我们就有了以下代码:
Date& Date::operator+=(int day)
{_day += day;while (_day > GetMonthDay(_year, _month)){_day -= GetMonthDay(_year, _month);++_month;if (_month == 13){++_year;_month = 1;}}return *this;
}
注意:我们看到,此时我们是用引用返回 ,在前面我们知道,返回对象生命周期到了会析构我们就传值返回;返回对象生命周期到了不会析构我们就传引用返回。而此时我们要获取的是加完之后的值,因此我们要传引用返回
日期的减等
日期减等的逻辑与加等类似,即:将天数全部减去后,如果天数小于0那么就将前一个月的天数加上去,如果月份为0那么就置为12,此时年份退一,于是我们便有了以下代码:
Date& Date::operator-=(int day)
{_day -= day;while (_day <= 0){--_month;if (_month == 0){_month == 12;--_year;}_day += GetMonthDay(_year, _month);}return *this;
}
日期的加减
下面我将来实现日期的加减,这里的加减指:不改变原有日期,在加或减相关日期后,返回我们所需要的日期,此时由于出了作用域要析构,所以是用传值返回,同时由于我们前面实现了加等与减等,在这里我们只需要复用上述逻辑即可轻松完成,代码如下:
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;
}
日期的加加减减
由于函数重载的规则,我们在重载++或--操作符时,为了区分是前置还是后置,C++规定:后置++或后置--多了一个int的操作数加以区分。由于前置++或前置--返回的是++或--之后的值,因此我们要传引用返回;而后置则返回之前的值,所以传值返回,于是我们可以写下以下代码:
//前置++
Date& Date::operator++()
{return *this += 1;
}
//后置++
Date Date::operator++(int)
{Date tmp = *this;return tmp += 1;
}
//前置--
Date& Date::operator--()
{return *this -= 1;
}
//后置--
Date Date::operator--(int)
{Date tmp = *this;return tmp -= 1;
}
日期的相减
日期的相减返回的是两日期之前的差值,所以用int返回,我们可以写下以下代码:
int Date::operator-(const Date& d)const
{Date max = *this;Date min = d;int flag = 1;int n = 0;if (*this < d){max = d;min = *this;}while (min != max){++min;++n;}return n * flag;
}
至此,一个完整的日期类就呈现了出来。
流插入和提取的重载
但是我们在Date.h文件中还有以下两个没有实现,那这两个是什么东西呢?
ostream& operator<<(ostream& out, const Date& d);
istream& operator>>(istream& in, Date& d);
实际上,这是流插入和流提取操作符的重载,那么为什么要用流插入和流提取呢?实际上是因为:C++中的流插入和提取存在缺陷。我们发现与C语言中的printf和scanf不同的是,cout和cin可以自动识别变量的类型,但这仅仅只能识别内置类型,自定义类型的输入和输出时不能直接使用cout和cin,那么我们该怎么解决这个问题呢?于是C++语法上支持了<<和>>的重载。但是为什么我们选择将其定义在类的外面呢?实际上是为了可读性所采取的操作,但是我们将其定义在类外无法访问私有,为了解决这个问题我们引入了友元这个概念。
友元
什么是友元呢?友元提供了一种突破封装的方式,有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不宜过多使用。
我们还是回归上述问题,为什么将其重载为成员函数会降低可读性呢?我们现在尝试去重载operator<<,然后发现没办法将operator<<重载成成员函数。因为cout的输出流对 象和隐含的this指针在抢占第一个参数的位置。this指针默认是第一个参数也就是左操作数了。但是实际使用 中cout需要是第一个形参对象,才能正常使用。所以要将operator<<重载成全局函数。但又会导致类外没办 法访问成员,此时就需要友元来解决。operator>>同理。
友元的特点
1.友元函数可访问类的私有和保护成员,但不是类的成员函数 友元函数不能用const修饰
2.友元函数可以在类定义的任何地方声明,不受类访问限定符限制
3.一个函数可以是多个类的友元函数
4.友元函数的调用与普通函数的调用原理相同
在有了上述知识的铺垫后,我们可以将流插入流提取操作符的重载函数写出,代码如下:
ostream& operator<<(ostream& out, const Date& d)
{out << d._year << "年" << d._month << "月" << d._day << "日" << endl;return out;
}
istream& operator>>(istream& in, Date& d)
{cout << "请依次输入年月日:";in >> d._year >> d._month >> d._day;return in;
}
日期类代码
整个Date.c的代码如下:
bool Date::CheckDate()
{if (_month < 1 || _month>12||_day<1||_day>GetMonthDay(_year,_month)){return false;}else{return true;}
}
Date::Date(int year, int month, int day)
{_year = year;_month = month;_day = day;if (!CheckDate()){cout << "日期错误" << endl;}
}
void Date::Print()const
{cout << _year << "-" << _month << "-" << _day << endl;
}
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){if (_day < d._day){return true;}}}return false;
}
bool Date::operator<=(const Date& d)const
{return (*this == d || *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 _year == d._year && _month == d._month && _day == d._day;
}
bool Date::operator!=(const Date& d)const
{return !(*this == d);
}Date& Date::operator+=(int day)
{_day += day;while (_day > GetMonthDay(_year, _month)){_day -= GetMonthDay(_year, _month);++_month;if (_month > 12){_month = 1;++_year;}}return *this;
}
Date Date::operator+(int day)const
{Date tmp = *this;tmp += day;return tmp;
}
Date& Date::operator-=(int day)
{_day -= day;while (_day <= 0){--_month;_day += GetMonthDay(_year, _month);if (_month == 0){_month = 12;--_year;}}return *this;
}int Date::operator-(const Date& d)const
{Date max = *this;Date min = d;int flag = 1;int n = 0;if (*this < d){max = d;min = *this;}while (min != max){++min;++n;}return n * flag;
}Date& Date::operator++()
{return *this += 1;
}
Date Date::operator++(int)
{Date tmp = *this;return tmp += 1;
}
Date& Date::operator--()
{return *this -= 1;
}
Date Date::operator--(int)
{Date tmp = *this;return tmp -= 1;
}ostream& operator<<(ostream& out, const Date& d)
{out << d._year << "年" << d._month << "月" << d._day << "日" << endl;return out;
}
istream& operator>>(istream& in, Date& d)
{cout << "请依次输入年月日:";in >> d._year >> d._month >> d._day;return in;
}
总结
感谢您能在百忙之中抽出时间来看鄙人的文章,相信看完文章的你会对C++的类与对象有一个更清楚的认识,受限于本人的水平,可能文章中会存在一些问题,欢迎各位指正,如果本篇文章对您有所帮助的话,希望能给我点赞评论加关注,您的支持就是对我创作的最大鼓励。
相关文章:
C++从入门到入土(四)--日期类的实现
目录 前言 日期类的实现 日期的获取 日期的比较 const成员函数 日期的加减 日期的加等 日期的减等 日期的加减 日期的加加减减 日期的相减 流插入和提取的重载 友元 友元的特点 日期类代码 总结 前言 前面我们介绍了C中类和对象的相关知识和六个默认成员函数&…...

【香橙派系列教程】(七)香橙派下的Python3安装
【七】香橙派下的Python3安装 为接下来的Linux图像识别智能垃圾桶做准备。 图像处理使用京东SDK只支持pyhton和Java接口,目的是引入C语言的Python调用,感受大厂做的算法bug 此接口是人工智能接口,京东识别模型是通过训练后的模型,…...
贝叶斯优化算法(Bo)与门控循环单元(GRU)结合的预测模型(Bo-GRU)及其Python和MATLAB实现
### 背景 随着时间序列数据在各个领域(如金融、气象、医疗等)应用的日益广泛,如何准确地预测未来的数据点成为了一个重要的研究方向。长短期记忆网络(LSTM)和门控循环单元(GRU)作为深度学习模型…...

人工智能时代,程序员当如何保持核心竞争力?
目录 前言 一.AI辅助编程对程序员工作的影响 二.程序员应重点发展的核心能力 三.人机协作模式下的职业发展规划 结束语 前言 随着AIGC(如chatgpt、midjourney、claude等)大语言模型接二连三的涌现,AI辅助编程工具日益普及,程序…...

LMDrive 端到端闭环自动驾驶框架
LMDrive,一种新颖的语言引导的端到端闭环自动驾驶框架。LMDrive独特地处理和整合多模态传感器数据与自然语言指令,使车辆能够在现实的指令设置中与人类和导航软件进行交互。 LMDrive由两个主要部分组成: 1)一个视觉编码器&#x…...
P2045 方格取数加强版
Description 给定一个 n n n \times n nn 的矩阵,从左上角出发,可以往右或者往下走,每到达一个方格,就取走上面的数(取过后格子上的数会清零),一共要走 k k k 次,求取到的数之和…...
【Bigdata】OLAP的衡量标准
这是我父亲 日记里的文字 这是他的生命 留下留下来的散文诗 几十年后 我看着泪流不止 可我的父亲已经 老得像一个影子 🎵 许飞《父亲写的散文诗》 OLAP(联机分析处理)系统的衡量标准主要集中在以下几个方面:…...

关于DDOS攻击趋势及防护措施
随着互联网技术的飞速发展,网络安全问题日益成为企业不可忽视的重要议题。分布式拒绝服务(DDoS)攻击作为其中的典型代表,以其强大的破坏力和难以防范的特性,给企业的网络安全带来了巨大挑战。今天我们就来了解下当前DD…...
Apache Flink:一个开源流处理框架
文章目录 引言官网链接Flink 原理概述核心概念 基础使用环境搭建编写 Flink 程序注意事项 高级使用窗口操作状态后端复杂事件处理(CEP)与 Kafka 集成 优点结论 引言 Apache Flink 是一个开源流处理框架,专为高吞吐量、低延迟的实时数据处理设…...

Nginx 学习笔记
1. Nginx简介 Nginx 是一个高性能的Http和反向代理服务器。也是一个IMAP/POP3/SMTP等邮件代理服务器。 特点: 占有内存少并发能力强安装非常的简单配置文件非常简洁(还能够支持perl语法)Bug非常少启动特别容易,并且几乎可以做到…...

软甲测试定义和分类
软件测试定义 使用人工和自动手段来运行或测试某个系统的过程,其目的在于检验他是否满足规定的需求或弄清预期结果与实际结果之间的差别 软件测试目的 为了发现程序存在的代码或业务逻辑错误 – 第一优先级发现错误为了检验产品是否符合用户需求 – 跟用户要求实…...

Vue 3+Vite+Eectron从入门到实战系列之(二)一Elementplus及VueRouter的配置
为了后续开发方便,在没有 UI 设计师配合的情况下,让我们的界面更加美观,我们使用 elementplus 组件库,并配置路由。 删除不需要的默认文件夹及文件,src 配置如下 实现效果 安装 elementplus,vue-router npm install element-plus --save npm install vue-router --save在…...

STL-list
1.list 1. list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。 2. list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素。 3. l…...
2024 7.29~8.4 周报
一、上周工作 2024 7.22~7.28周报-CSDN博客 二、本周计划 修改论文 三、完成情况 3.1 论文修改 3.1.1 摘要 问题:所写问题是一般性的深度网络问题(过拟合),并没有针对FWI的问题(边缘不清晰、深层不清晰、速度慢…...

随身助手271个可用api接口网站php源码(随身助手API)
源码简介: 随身助手API,本次更新了271个可用接口,现在开源给大家使用,无后门无加密,放心使用。 {“标题”:”看图猜成语接口”,”小标题”:”随身助手API”,”地址”:”tianyi/LookIdiom.php”,”状态”:”正常”} {…...

珠江电缆,顺应全球变化,实现高质量出海
在全球经济快速变化的今天,越来越多的企业将目光投向了国际市场。特别是对于线缆行业来说,顺应全球变化、应对机遇与挑战,实现高质量出海已成为长期发展的战略目标之一。珠江电缆作为一家集研发、制造和销售为一体的大型专业电线电缆企业&…...

redis面试(四)持久化
什么是持久化? 由于redis是基于内存操作的轻量型数据库,所以如果发生宕机重启这种事情,存储的数据就会直接丢失,如果在里面存储了没有备份的数据,那么确实会对我们的业务造成一定影响。 所以我们要通过持久化的手段&a…...
构建数据桥梁:Pandas如何简化API到DataFrame的转换
在数据科学的广阔天地中,API如同一把钥匙,为我们打开了通往丰富数据资源的大门。无论是追踪最新的股市动态,还是分析社交媒体趋势,API都能提供我们需要的实时数据。今天,我们将一起探索如何利用Python的pandas库&#…...

echarts制作grafana 面板之折线图
最近有需求需要制作grafana 来实现自己的需求,于是开始研究 实现效果如下 实现代码 import * as echarts from echarts;var chartDom document.getElementById(main); var myChart echarts.init(chartDom, dark); var option;function getLast30Days() {let da…...

技术男的审美反击:UI配置化新纪元
之前常常被甲方的领导说,我们全是一群钢铁直男,一点不懂审美,其实我们心里边想的 “您说得对啊!!!!” 这个可能和理工科有关系吧,理工男好像都差不多,所以这次我们就把很…...

使用分级同态加密防御梯度泄漏
抽象 联邦学习 (FL) 支持跨分布式客户端进行协作模型训练,而无需共享原始数据,这使其成为在互联和自动驾驶汽车 (CAV) 等领域保护隐私的机器学习的一种很有前途的方法。然而,最近的研究表明&…...
c++ 面试题(1)-----深度优先搜索(DFS)实现
操作系统:ubuntu22.04 IDE:Visual Studio Code 编程语言:C11 题目描述 地上有一个 m 行 n 列的方格,从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子,但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…...
Java多线程实现之Callable接口深度解析
Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...

学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1
每日一言 生活的美好,总是藏在那些你咬牙坚持的日子里。 硬件:OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写,"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...

ETLCloud可能遇到的问题有哪些?常见坑位解析
数据集成平台ETLCloud,主要用于支持数据的抽取(Extract)、转换(Transform)和加载(Load)过程。提供了一个简洁直观的界面,以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...

04-初识css
一、css样式引入 1.1.内部样式 <div style"width: 100px;"></div>1.2.外部样式 1.2.1.外部样式1 <style>.aa {width: 100px;} </style> <div class"aa"></div>1.2.2.外部样式2 <!-- rel内表面引入的是style样…...
【JavaSE】绘图与事件入门学习笔记
-Java绘图坐标体系 坐标体系-介绍 坐标原点位于左上角,以像素为单位。 在Java坐标系中,第一个是x坐标,表示当前位置为水平方向,距离坐标原点x个像素;第二个是y坐标,表示当前位置为垂直方向,距离坐标原点y个像素。 坐标体系-像素 …...
python报错No module named ‘tensorflow.keras‘
是由于不同版本的tensorflow下的keras所在的路径不同,结合所安装的tensorflow的目录结构修改from语句即可。 原语句: from tensorflow.keras.layers import Conv1D, MaxPooling1D, LSTM, Dense 修改后: from tensorflow.python.keras.lay…...
使用Matplotlib创建炫酷的3D散点图:数据可视化的新维度
文章目录 基础实现代码代码解析进阶技巧1. 自定义点的大小和颜色2. 添加图例和样式美化3. 真实数据应用示例实用技巧与注意事项完整示例(带样式)应用场景在数据科学和可视化领域,三维图形能为我们提供更丰富的数据洞察。本文将手把手教你如何使用Python的Matplotlib库创建引…...
C++.OpenGL (14/64)多光源(Multiple Lights)
多光源(Multiple Lights) 多光源渲染技术概览 #mermaid-svg-3L5e5gGn76TNh7Lq {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-3L5e5gGn76TNh7Lq .error-icon{fill:#552222;}#mermaid-svg-3L5e5gGn76TNh7Lq .erro…...