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

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接口&#xff0c;目的是引入C语言的Python调用&#xff0c;感受大厂做的算法bug 此接口是人工智能接口&#xff0c;京东识别模型是通过训练后的模型&#xff0c;…...

贝叶斯优化算法(Bo)与门控循环单元(GRU)结合的预测模型(Bo-GRU)及其Python和MATLAB实现

### 背景 随着时间序列数据在各个领域&#xff08;如金融、气象、医疗等&#xff09;应用的日益广泛&#xff0c;如何准确地预测未来的数据点成为了一个重要的研究方向。长短期记忆网络&#xff08;LSTM&#xff09;和门控循环单元&#xff08;GRU&#xff09;作为深度学习模型…...

人工智能时代,程序员当如何保持核心竞争力?

目录 前言 一.AI辅助编程对程序员工作的影响 二.程序员应重点发展的核心能力 三.人机协作模式下的职业发展规划 结束语 前言 随着AIGC&#xff08;如chatgpt、midjourney、claude等&#xff09;大语言模型接二连三的涌现&#xff0c;AI辅助编程工具日益普及&#xff0c;程序…...

LMDrive 端到端闭环自动驾驶框架

LMDrive&#xff0c;一种新颖的语言引导的端到端闭环自动驾驶框架。LMDrive独特地处理和整合多模态传感器数据与自然语言指令&#xff0c;使车辆能够在现实的指令设置中与人类和导航软件进行交互。 LMDrive由两个主要部分组成&#xff1a; 1&#xff09;一个视觉编码器&#x…...

P2045 方格取数加强版

Description 给定一个 n n n \times n nn 的矩阵&#xff0c;从左上角出发&#xff0c;可以往右或者往下走&#xff0c;每到达一个方格&#xff0c;就取走上面的数&#xff08;取过后格子上的数会清零&#xff09;&#xff0c;一共要走 k k k 次&#xff0c;求取到的数之和…...

【Bigdata】OLAP的衡量标准

这是我父亲 日记里的文字 这是他的生命 留下留下来的散文诗 几十年后 我看着泪流不止 可我的父亲已经 老得像一个影子 &#x1f3b5; 许飞《父亲写的散文诗》 OLAP&#xff08;联机分析处理&#xff09;系统的衡量标准主要集中在以下几个方面&#xff1a;…...

关于DDOS攻击趋势及防护措施

随着互联网技术的飞速发展&#xff0c;网络安全问题日益成为企业不可忽视的重要议题。分布式拒绝服务&#xff08;DDoS&#xff09;攻击作为其中的典型代表&#xff0c;以其强大的破坏力和难以防范的特性&#xff0c;给企业的网络安全带来了巨大挑战。今天我们就来了解下当前DD…...

Apache Flink:一个开源流处理框架

文章目录 引言官网链接Flink 原理概述核心概念 基础使用环境搭建编写 Flink 程序注意事项 高级使用窗口操作状态后端复杂事件处理&#xff08;CEP&#xff09;与 Kafka 集成 优点结论 引言 Apache Flink 是一个开源流处理框架&#xff0c;专为高吞吐量、低延迟的实时数据处理设…...

Nginx 学习笔记

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

软甲测试定义和分类

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

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是可以在常数范围内在任意位置进行插入和删除的序列式容器&#xff0c;并且该容器可以前后双向迭代。 2. list的底层是双向链表结构&#xff0c;双向链表中每个元素存储在互不相关的独立节点中&#xff0c;在节点中通过指针指向其前一个元素和后一个元素。 3. l…...

2024 7.29~8.4 周报

一、上周工作 2024 7.22~7.28周报-CSDN博客 二、本周计划 修改论文 三、完成情况 3.1 论文修改 3.1.1 摘要 问题&#xff1a;所写问题是一般性的深度网络问题&#xff08;过拟合&#xff09;&#xff0c;并没有针对FWI的问题&#xff08;边缘不清晰、深层不清晰、速度慢…...

随身助手271个可用api接口网站php源码(随身助手API)

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

珠江电缆,顺应全球变化,实现高质量出海

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

redis面试(四)持久化

什么是持久化&#xff1f; 由于redis是基于内存操作的轻量型数据库&#xff0c;所以如果发生宕机重启这种事情&#xff0c;存储的数据就会直接丢失&#xff0c;如果在里面存储了没有备份的数据&#xff0c;那么确实会对我们的业务造成一定影响。 所以我们要通过持久化的手段&a…...

构建数据桥梁:Pandas如何简化API到DataFrame的转换

在数据科学的广阔天地中&#xff0c;API如同一把钥匙&#xff0c;为我们打开了通往丰富数据资源的大门。无论是追踪最新的股市动态&#xff0c;还是分析社交媒体趋势&#xff0c;API都能提供我们需要的实时数据。今天&#xff0c;我们将一起探索如何利用Python的pandas库&#…...

echarts制作grafana 面板之折线图

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

技术男的审美反击:UI配置化新纪元

之前常常被甲方的领导说&#xff0c;我们全是一群钢铁直男&#xff0c;一点不懂审美&#xff0c;其实我们心里边想的 “您说得对啊&#xff01;&#xff01;&#xff01;&#xff01;” 这个可能和理工科有关系吧&#xff0c;理工男好像都差不多&#xff0c;所以这次我们就把很…...

Python爬虫实战:研究MechanicalSoup库相关技术

一、MechanicalSoup 库概述 1.1 库简介 MechanicalSoup 是一个 Python 库,专为自动化交互网站而设计。它结合了 requests 的 HTTP 请求能力和 BeautifulSoup 的 HTML 解析能力,提供了直观的 API,让我们可以像人类用户一样浏览网页、填写表单和提交请求。 1.2 主要功能特点…...

在四层代理中还原真实客户端ngx_stream_realip_module

一、模块原理与价值 PROXY Protocol 回溯 第三方负载均衡&#xff08;如 HAProxy、AWS NLB、阿里 SLB&#xff09;发起上游连接时&#xff0c;将真实客户端 IP/Port 写入 PROXY Protocol v1/v2 头。Stream 层接收到头部后&#xff0c;ngx_stream_realip_module 从中提取原始信息…...

OkHttp 中实现断点续传 demo

在 OkHttp 中实现断点续传主要通过以下步骤完成&#xff0c;核心是利用 HTTP 协议的 Range 请求头指定下载范围&#xff1a; 实现原理 Range 请求头&#xff1a;向服务器请求文件的特定字节范围&#xff08;如 Range: bytes1024-&#xff09; 本地文件记录&#xff1a;保存已…...

Nuxt.js 中的路由配置详解

Nuxt.js 通过其内置的路由系统简化了应用的路由配置&#xff0c;使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...

Android 之 kotlin 语言学习笔记三(Kotlin-Java 互操作)

参考官方文档&#xff1a;https://developer.android.google.cn/kotlin/interop?hlzh-cn 一、Java&#xff08;供 Kotlin 使用&#xff09; 1、不得使用硬关键字 不要使用 Kotlin 的任何硬关键字作为方法的名称 或字段。允许使用 Kotlin 的软关键字、修饰符关键字和特殊标识…...

代码随想录刷题day30

1、零钱兑换II 给你一个整数数组 coins 表示不同面额的硬币&#xff0c;另给一个整数 amount 表示总金额。 请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额&#xff0c;返回 0 。 假设每一种面额的硬币有无限个。 题目数据保证结果符合 32 位带…...

安全突围:重塑内生安全体系:齐向东在2025年BCS大会的演讲

文章目录 前言第一部分&#xff1a;体系力量是突围之钥第一重困境是体系思想落地不畅。第二重困境是大小体系融合瓶颈。第三重困境是“小体系”运营梗阻。 第二部分&#xff1a;体系矛盾是突围之障一是数据孤岛的障碍。二是投入不足的障碍。三是新旧兼容难的障碍。 第三部分&am…...

android RelativeLayout布局

<?xml version"1.0" encoding"utf-8"?> <RelativeLayout xmlns:android"http://schemas.android.com/apk/res/android"android:layout_width"match_parent"android:layout_height"match_parent"android:gravity&…...

SQL Server 触发器调用存储过程实现发送 HTTP 请求

文章目录 需求分析解决第 1 步:前置条件,启用 OLE 自动化方式 1:使用 SQL 实现启用 OLE 自动化方式 2:Sql Server 2005启动OLE自动化方式 3:Sql Server 2008启动OLE自动化第 2 步:创建存储过程第 3 步:创建触发器扩展 - 如何调试?第 1 步:登录 SQL Server 2008第 2 步…...

DeepSeek源码深度解析 × 华为仓颉语言编程精粹——从MoE架构到全场景开发生态

前言 在人工智能技术飞速发展的今天&#xff0c;深度学习与大模型技术已成为推动行业变革的核心驱动力&#xff0c;而高效、灵活的开发工具与编程语言则为技术创新提供了重要支撑。本书以两大前沿技术领域为核心&#xff0c;系统性地呈现了两部深度技术著作的精华&#xff1a;…...