【是C++,不是C艹】 手把手带你实现Date类(附源码)

💞💞欢迎来到 Claffic 的博客💞💞

 👉 专栏:《是C++,不是C艹》👈

前言:

恍惚间,已经两个月没更新了 (;´д`)ゞ 我忏悔... 

但C++的学习不能停止!这期带大家实践一波,手把手教大家实现一个Date类,以感受C++类的魅力

注:

你最好是学完了C语言,并学过一些初阶的数据结构。


(没有目录) ヽ( ̄ω ̄( ̄ω ̄〃)ゝ 

Part1:一个引子

🌰我猜你用过倒数日:

🪄这其实是一种简单的日期机算器,用来计算某事件 已经 / 还有 多少天

那么我们搜索 日期计算器 会看到它的两大功能:

日期推算:

计算日期差:

 

❓那么这些功能是怎么实现的呢?

接下来就由我来带你揭开它的神秘面纱!

Part2:思路

1.日期推算

❓你想,先扔给你一个日期,再给你一个整数(正往后,负往前),你会怎么推算新日期?

简单情况:日相加,得到的日部分不超过当前月份的天数,就如 2023-8-21 与 1,得 2023-8-22 。

进位情况:日相加,得到的日部分超出当前月份的天数,给月进位,如 2023-8-21 与 12,得                            2023-9-2;

                  另有月满13,需要月重置1再给年进位,如 2023-8-21 与 133,得 2024-1-1

🚨注意还要考虑闰年 非闰年:闰年2月有29日  非闰年2月有28日。

2.计算日期差

❓再想,扔给你两个日期,你怎么计算两者之间间隔了多少天?

你是不是这样想的:年先做差,月再做差,日再做差,然后都换算成日,最后相加?

嗯,这是很直接的思路,可以,但没必要。

✅这里提供另一种思路

两个日期,必然有大有小(暂时忽略相等),找出较小者,让较小者往较大者增长,每次增加一日(++),加个计数器,计出来的就是两者的日期差。

或许你会感到麻烦,那你还是C语言的思维!

📢别忘了:C++是面向对象的语言!我们完全可以创建一个日期类来完成相关的计算和比较

Part3:实现

1.创建类

一个日期,包含年月日三个基本数据,这些不需要对用户公开,私有即可:

class Date
{
public:// todoprivate:int _year;int _month;int _day;
};

另外添加一些基本的方法

先来解决最基础的问题:那就是每个月有几天

我们不妨来封装一个方法,来获取这个月里有几天:

int Date::GetMonthDay(int year, int month) // 年份是来判断是否是闰年的
{// 第一个元素无实际效用,但就能保证 下标 == 月数static int arr[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 }; // 一三五七八十腊...// 2月前提下再判断年的情况,减少消耗if (month == 2 && ((year % 4 == 0) && (year % 100 != 0) || (year % 400 == 0))) return 29;elsereturn arr[month];
}

构造:

Date::Date(int year, int month, int day)
{// 判断合法性 if (month > 0 && month < 13&& day > 0 && day < GetMonthDay(year, month)){_year = year;_month = month;_day = day;}else{cout << "非法日期" << endl;assert(false);}
}

这里可以给一个全缺省,不给数据就默认是这个日期,还蛮香~

Date(int year = 2008, int month = 1, int day = 1);

展示:

// 代码较短,在类中写就OK,不用跨文件了
void Print() const
{cout << _year << "-" << _month << "-" << _day << endl;
}

2.日期推算

⚔️我们期望得到这样的效果:

void DateTest1()
{Date d1(2023,8,21);Date d2 = d1 + 133;d1.Print();d2.Print();
}

👁️‍🗨️运行结果:

我们知道,普通的 + 是对整型,浮点型,字符型等内置类型起效的,而这里的 + 在 Date 和 int 之间起作用了,为甚?

没错,这就是C++大名鼎鼎的运算符重载!(其实 = 也重载了)

// 日期 + 天数
Date Date::operator+(int day)
{Date tmp(*this); // + 是不修改本身的!所以要先拷贝一份,最后返回的是拷贝修改后的内容tmp._day += day;// 月与年的进位while (tmp._day > GetMonthDay(tmp._year, tmp._month)){tmp._day -= GetMonthDay(tmp._year, tmp._month);tmp._month++;if (tmp._month == 13){tmp._year++;tmp._month = 1;}}return tmp;
}
// 赋值运算符重载
Date& Date::operator=(const Date& d) // 原内容无需修改,加const
{_day = d._day;_month = d._month;_year = d._year;return *this;
}

3.计算日期差

⚔️预期效果:

void DateTest3()
{Date d5(2023, 8, 21);Date d6(2004, 3, 30);cout << d5 - d6 << endl;cout << d6 - d5 << endl;
}

👁️‍🗨️运行结果:

很明显,这是重载了 - ,使得 - 能在两个 Date 类之间起作用

🗡️按照二趴提供的思路,写下这样的代码:

int Date::operator-(const Date& d) const
{Date max = *this;Date min = d;int flag = 1; // 巧妙的flag,调节最后结果的正负if (*this < d){max = d;min = *this;flag = -1;}int n = 0;while (min != max){++min;++n;}return n * flag;
}

🗡️我们发现,里面的 <, !=, ++ 都是需要针对 Date 进行重载的:

// <运算符重载
bool Date::operator<(const Date& d) const
{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;}return false;
}
// ==运算符重载
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++()
{*this += 1; // 还需重载 +=return *this;
}// 日期+=天数
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;
}

经过一系列重载后,就可以达到计算日期差的效果咯!

Part4:其他功能

1.输入输出

❓在实现了两种主要的功能之后,既然把 Date 当作一个类了,那为甚马不能搞得像输入一个整数那样进行输入呢?

⚔️预期效果

void DateTest4()
{Date d7;cin >> d7;cout << d7 << endl;
}

👁️‍🗨️运行结果:

🗡️没错,这需要将 cin 和 cout 进行重载:

istream& operator>>(istream& in, Date& d)
{int year, month, day;in >> year >> month >> day;if (month > 0 && month < 13&& day > 0 && day < d.GetMonthDay(year, month)){d._year = year;d._month = month;d._day = day;}else{cout << "非法日期" << endl;assert(false);}return in;
}
ostream& operator<<(ostream& out, const Date& d)
{out << d._year << "年" << d._month << "月" << d._day << "日" << endl;return out;
}

但是这样还不行,因为 cin 修改了私有的数据,哪有什么办法能让这个重载能访问私有的数据呢?

对,那就是友元

可以在类中声明,这个重载是类的朋友,允许他访问私密~

friend ostream& operator<<(ostream& out, const Date& d);
friend istream& operator>>(istream& in, Date& d);

2.前置和后置

❓我们知道,++ / -- 是有前置和后置之分的,那么在重载中前置和后置又是怎么区分的呢?

这里就以 ++ 为例吧:

// 前置++
Date& Date::operator++()
{*this += 1;return *this;
}// 后置++
Date Date::operator++(int)
{Date tmp = *this;*this += 1;return tmp;
}

前置:先计算,后使用

后置:先使用,后计算

实现中,后置事先拷贝了自身,返回的还是原来的值,做到了后计算;而前置直接修改自身,返回自身,做到了先计算;

传参中,后置用 int 来与前置重载做区分,语法这样规定;

返回值上,后置返回类型,前置返回引用。

源码在此

Date.h:

#pragma once
#include<iostream>
#include<cassert>
using namespace std;class Date
{friend ostream& operator<<(ostream& out, const Date& d);friend istream& operator>>(istream& in, Date& d);
public:// 获取某年某月的天数int GetMonthDay(int year, int month);void Print() const{cout << _year << "-" << _month << "-" << _day << endl;}// 全缺省的构造函数Date(int year = 2008, int month = 1, int day = 1);// 赋值运算符重载Date& operator=(const Date& d);// 日期+=天数Date& operator+=(int day);// 日期+天数Date operator+(int day);// 日期-天数Date operator-(int day);// 日期-=天数Date& operator-=(int day);// 前置++Date& operator++();// 后置++Date operator++(int);// 后置--Date operator--(int);// 前置--Date& operator--();// >运算符重载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;// 日期-日期 返回天数int operator-(const Date& d) const;private:int _year;int _month;int _day;
};

Date.cpp:

#define _CRT_SECURE_NO_WARNINGS 1
#include"Date.h"// 获取某年某月的天数
int Date::GetMonthDay(int year, int month)
{static int arr[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;elsereturn arr[month];
}// 全缺省的构造函数
Date::Date(int year, int month, int day)
{if (month > 0 && month < 13&& day > 0 && day < GetMonthDay(year, month)){_year = year;_month = month;_day = day;}else{cout << "非法日期" << endl;assert(false);}
}// 赋值运算符重载
Date& Date::operator=(const Date& d)
{_day = d._day;_month = d._month;_year = d._year;return *this;
}// <运算符重载
bool Date::operator<(const Date& d) const
{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;}return false;
}// ==运算符重载
bool Date::operator==(const Date& d) const
{return _year == d._year&& _month == d._month&& _day == d._day;
}// >=运算符重载 d1 >= d2
bool Date::operator >= (const Date& d) const
{return !(*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 !(*this == d);
}// 日期+=天数
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;
}// 日期+天数
Date Date::operator+(int day)
{Date tmp(*this);tmp._day += day;while (tmp._day > GetMonthDay(tmp._year, tmp._month)){tmp._day -= GetMonthDay(tmp._year, tmp._month);tmp._month++;if (tmp._month == 13){tmp._year++;tmp._month = 1;}}return tmp;
}// 日期-=天数
Date& Date::operator-=(int day)
{_day -= day;while (_day <= 0){_day += GetMonthDay(_year, _month);_month--;if (_month == 0){_year--;_month = 12;}}return *this;
}// 日期-天数
Date Date::operator-(int day)
{Date tmp = *this;tmp._day -= day;while (tmp._day <= 0){tmp._day += GetMonthDay(tmp._year, tmp._month);tmp._month--;if (tmp._month == 0){tmp._year--;tmp._month = 12;}}return tmp;
} // 前置++
Date& Date::operator++()
{*this += 1;return *this;
}// 后置++
Date Date::operator++(int)
{Date tmp = *this;*this += 1;return tmp;
}// 前置--
Date& Date::operator--()
{*this -= 1;return *this;
}// 后置--
Date Date::operator--(int)
{Date tmp = *this;*this -= 1;return tmp;
}// 日期-日期 返回天数
int Date::operator-(const Date& d) const
{Date max = *this;Date min = d;int flag = 1;if (*this < d) // C艹,多了个分号 -- bug 记录{max = d;min = *this;flag = -1;}int n = 0;while (min != max){++min;++n;}return n * flag;
}ostream& operator<<(ostream& out, const Date& d)
{out << d._year << "年" << d._month << "月" << d._day << "日" << endl;return out;
}istream& operator>>(istream& in, Date& d)
{int year, month, day;in >> year >> month >> day;if (month > 0 && month < 13&& day > 0 && day < d.GetMonthDay(year, month)){d._year = year;d._month = month;d._day = day;}else{cout << "非法日期" << endl;assert(false);}return in;
}

 

代码已上传至 我的 gitee

拿走不谢~


总结: 

实现 Date 类,并没有那么难,明确类的特征,捕捉到必要数据,再进行方法的实现即可,这次用了不少运算符重载。

码文不易 

如果你觉得这篇文章还不错并且对你有帮助,不妨支持一波哦  💗💗💗

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/46866.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Redis 持久化的手段有哪些 ?RDB 和 AOF 有什么区别 ?

目录 1. Redis 持久化的手段有哪些 2. RDB 和 AOF 有什么区别 2.1 RDB 持久化 2.2 AOF 持久化 2.2.1 AOF 持久化策略有哪些 3. 混合持久化是如何执行的&#xff08;了解&#xff09; 1. Redis 持久化的手段有哪些 Redis 持久化的手段有三种&#xff1a; 快照方式&#…

开源数据库Mysql_DBA运维实战 (总结)

开源数据库Mysql_DBA运维实战 &#xff08;总结&#xff09; SQL语句都包含哪些类型 DDL DCL DML DQL Yum 安装MySQL的配置文件 配置文件&#xff1a;/etc/my.cnf日志目录&#xff1a;/var/log/mysqld.log错误日志&#xff1a;/var/log/mysql/error.log MySQL的主从切换 查看主…

什么是程序化交易接口?执行三步曲是什么?

在股市中的发展过程中&#xff0c;通过不断的更新迭代&#xff0c;从手动交易到自动交易的过程就有了历史的蜕变&#xff0c;那么对于程序化交易接口&#xff08;Application Programming Interface, API&#xff09;其实就是指为程序化交易提供的一组定义和规范&#xff0c;允…

物联网工程应用实训室建设方案

一、物联网工程应用系统概述 1.1物联网工程定义 物联网工程&#xff08;Internet of Things Engineering&#xff09;是一种以信息技术&#xff08;IT&#xff09;来改善实体世界中人们生活方式的新兴学科&#xff0c;它利用互联网技术为我们的日常生活活动提供服务和增益&am…

时序预测 | MATLAB实现基于CNN-LSTM卷积长短期记忆神经网络的时间序列预测-递归预测未来(多指标评价)

时序预测 | MATLAB实现基于CNN-LSTM卷积长短期记忆神经网络的时间序列预测-递归预测未来(多指标评价) 目录 时序预测 | MATLAB实现基于CNN-LSTM卷积长短期记忆神经网络的时间序列预测-递归预测未来(多指标评价)预测结果基本介绍程序设计参考资料 预测结果 基本介绍 MATLAB实现基…

线程面试题-1

看的博客里面总结的线程的八股文 1、线程安全的集合有哪些&#xff1f;线程不安全的呢&#xff1f; 线程安全的&#xff1a; Hashtable&#xff1a;比HashMap多了个线程安全。 ConcurrentHashMap:是一种高效但是线程安全的集合。 Vector&#xff1a;比Arraylist多了个同步化…

安装Vue_dev_tools

Vue控制台出现Download the Vue Devtools extension for a better development experience: 下载Vue_dev_tools,这里给出网盘链接&#xff0c;有Vue2和Vue3的&#xff0c;dev_tools 以Google浏览器为例 点击设置&#xff08;就是那三个点&#xff09;->扩展程序->管理扩…

游戏服务端性能测试

导语&#xff1a;近期经历了一系列的性能测试&#xff0c;涵盖了Web服务器和游戏服务器的领域。在这篇文章中&#xff0c;我将会对游戏服务端所做的测试进行详细整理和记录。需要注意的是&#xff0c;本文着重于记录&#xff0c;而并非深入的编程讨论。在这里&#xff0c;我将与…

C# Windows登录界面进行截图,控制鼠标键盘等操作实现(一)

首先常规的账户进程是没办法获取登录界面的信息的&#xff0c;因为登录界面已经不在某个账户下了&#xff0c;登录界面显示了每一个账户的切换。所以得使用System权限的进程。 那么Windows系统究竟是怎么将登录界面与用户桌面隔离开的呢&#xff1f;首先先通过一些Windows操作系…

很好的启用window10专业版系统自带的远程桌面

启用window10专业版系统自带的远程桌面 文章目录 启用window10专业版系统自带的远程桌面前言1.找到远程桌面的开关2. 找到“应用”项目3. 打开需要远程操作的电脑远程桌面功能 总结 前言 Windows操作系统作为应用最广泛的个人电脑操作系统&#xff0c;在我们身边几乎随处可见。…

vite初始化vue3项目(配置自动格式化工具与git提交规范工具)

初始化项目 vite构建vue项目还是比较简单的&#xff0c;简单配置选择一下就行了 初始化命令 npm init vuelatest初始化最新版本vue项目 2. 基本选项含义 Add TypeScript 是否添加TSADD JSX是否支持JSXADD Vue Router是否添加Vue Router路由管理工具ADD Pinia 是否添加pinia…

Nginx反向代理配置流式响应

Nginx 是通过缓存响应内容来处理请求的。也就是说&#xff0c;当 Nginx 接收到完整的响应后&#xff0c;才会将其发送给客户端&#xff0c;所以默认不支持流式响应&#xff0c;这里讲讲 Nginx 反向代理中怎么配置流式响应&#xff1f; 一、使用背景 最近使用 Egg.js 搭建自动化…

【腾讯云 TDSQL-C Serverless 产品测评】全面测评TDSQL-C Mysql Serverless

全面测评TDSQL-C Mysql Serverless 文章目录 全面测评TDSQL-C Mysql Serverless前言什么是TDSQL-C Mysql Serverless初始化 TDSQL-C Mysql Serverless新建数据库建立数据表开启外网访问 兼容性SQL文件 导入导出navicat 直接在线传输 构建测试环境准备Python测试脚本准备 Jmeter…

下载安装并使用小乌龟TortoiseGit

1、下载TortoiseGit安装包 官网&#xff1a;Download – TortoiseGit – Windows Shell Interface to Githttps://tortoisegit.org/download/ 2、小乌龟汉化包 在官网的下面就有官方提供的下载包 3、安装

Electron入门,项目运行,只需四步轻松搞定。

electron 简单介绍&#xff1a; 实现&#xff1a;HTML/CSS/JS桌面程序&#xff0c;搭建跨平台桌面应用。 electron 官方文档&#xff1a; [https://electronjs.org/docs] 本文是基于以下2篇文章且自行实践过的&#xff0c;可行性真实有效。 文章1&#xff1a; https://www.cnbl…

Django学习笔记(2)

创建app 属于自动执行了python manage.py 直接在里面运行startapp app01就可以创建app01的项目了 之后在setting.py中注册app01 INSTALLED_APPS ["django.contrib.admin","django.contrib.auth","django.contrib.contenttypes","django.c…

合并jar包导致gradle传递依赖失效

目录 零、背景一、合并jar包1.1、自定义一组jar包1.2、自定义合并jar的任务1.3、定义打包jar的任务 二、发布jar包2.1、未合并jar包之前的合并方式2.2、合并jar包之后的合并方式 三、发现问题3.1、确定gradle中的依赖关系3.2、对比maven是否缺失依赖3.3、对比合并前后的pom.xml…

Source Insight配置Cppcheck做静态测试(Windows)

1.安装cppcheck 先从cppcheck官方网站下载cppcheck的安装包。 注&#xff1a; &#xff08;1&#xff09;官网地址&#xff1a;https://sourceforge.net/projects/cppcheck &#xff08;2&#xff09;截止2023年8月&#xff0c;官方发布的最新版本是cppcheck-2.11-x64-Setup.…

子集-回溯方法

class Solution {//题解思路//LinkedList<Integer> path new LinkedList<>(); List<List<Integer>> results new ArrayList<>();public List<List<Integer>> subsets(int[] nums) {//主方法中调用方法同时传入指定的参数初始值bac…

【数据挖掘】使用 Python 分析公共数据【01/10】

一、说明 本文讨论了如何使用 Python 使用 Pandas 库分析官方 COVID-19 病例数据。您将看到如何从实际数据集中收集见解&#xff0c;发现乍一看可能不那么明显的信息。特别是&#xff0c;本文中提供的示例说明了如何获取有关疾病在不同国家/地区传播速度的信息。 二、准备您的…