C++ —— 实现一个日期类
目录
一. 对日期类的介绍
二. 实现日期类
1. 运算符重载
2.日期类实现代码
一. 对日期类的介绍
通过对类和对象(这里链接是类和对象的介绍)的学习,类就是一种模型一样的东西,是对象一种抽象的描述。所以实现日期类,就是实现一个日期模型,里面有所对应的成员变量(属性:年/月/日),还有一些它的方法(成员函数:打印该日期、对日期加减等)。
二. 实现日期类
在实现日期类之前,我们还需要了解一下运算符重载,这是为了后续我们对日期的加、减天数以及日期减日期等做准备,因为当运算符被用于类类型的对象时,需要我们去重载运算符去指定新的含义。(C++规定类类型对象使用运算符时,必须转换成调用对应运算符重载,若没有对应的运算符重载,则会编译报错。)
//内置类型
int i = 1;
int j = 2;
int sum = 0;i = i + 1;
sum = i - j;//自定义类型对象若想用类似上面的就需要在类里面声明定义对应的运算符重载函数
1. 运算符重载
C++规定类类型对象使用运算符时,必须转换成对应运算符重载,若没有对应的运算符重载,则会编译报错。
也就是运算符被用于类类型的对象时,可以通过运算符重载的形式指定新的含义。
运算符重载是一个函数,它的名字是由关键字operator和运算符组成,也具有返回类型、参数列表、函数体。
class A
{
public:A(int a = 1, int b = 1):_a(a),_b(b){}//这里就是赋值运算符重载A& operator=(const A& a){_a = a._a;_b = a._b;return *this;}
private:int _a;int _b;
};int main()
{A a1;A a2(5, 2);a1 = a2;return 0;
}
我们来查看验证是否按照指定新的含义进行:
赋值之前:
赋值之后:
可以看到a1里的成员确实被a2里的成员变量赋值了。
注意:
//上述代码中a1 = a2;
//其实详细写法是:
a1.operator=(a2);//但它们俩意义相同都是为了调用赋值运算符重载
一元运算符只有一个参数,二元运算符要有两个参数。(注意:二元运算符的左侧运算对象传给第一个参数,右侧运算对象传给第二个参数)。
也就是说,重载运算符函数的参数个数和该运算符作用的运算对象数量一致的。
上面我们看到A类重载=运算符只写了一个函数参数,这是因为它是成员函数,第一个参数默认传了this指针(类和对象有介绍)。
注意:重载运算符必须至少有一个类类型的形参,不能通过运算符重载改变内置类型对象的含义。
若不是成员函数,就按照上面规则来:
class A
{
public:A(int a = 1, int b = 1):_a(a),_b(b){}A& operator=(const A& a){_a = a._a;_b = a._b;return *this;}int _a;int _b;
};//注意:operator + 必须至少有一个类类型的形参,否则会报错,就比如下面注释掉的情况
//int operator(int a,int n)
//{
// return a-n;
//}int operator+(const A& a, int& n)
{return a._a + n; //这里成员变量需要放公有,否则不能直接访问
}int main()
{A a;int n = 10;cout << a + n << endl;return 0;
}
运算符重载以后,优先级和结合性与对应的内置类型运算符保持一致的。
不能重载的情况:
- 不能用没有的符号创建新的操作符(运算符)
//例如: operator@ //就不能创建
-
有五个运算符是不能重载的:.* :: sizeof ?: .
后四个是经常用到的,第一个非常用,下面单独拿出来解释
//函数指针较为特别,typedef类型需要这样写
typedef void (*PF)(); class A
{
public:void func(){cout << "A::func()" << endl;}
};//若typedef成员函数指针类型,就加个指定类域即可
typedef void(A::*PF)();// .*运算符就用于以下方式回调函数的场景(成员函数回调)
int main()
{//成员函数要加&才能取到函数指针PF pf = &A::func; //这里相当于 void(A::*pf)() = &A::func;A a;(a.*pf)();return 0;
}
(深入理解指针)
//这里是普通全局函数回调的形式
(*pf)();
所以这个符号的意义是对象调用成员函数指针时,成员函数的回调 (注意:考虑有隐含的this指针,不能显示写形参和传实参)
重载++运算符时,前置++与后置++的运算符重载函数名都是operator++,无法来区分。 C++规定,后置++重载时,增加一个int形参,跟前置++构成函数重载,方便区分。
class Date
{
public://构造函数Date(int year = 1900,int month =1,int day=1):_year(year),_month(month),_day(day){}//前置++Date& operator++(){cout << "这里是前置++" << endl; //这里演示一下,先不实现return *this;}//后置++Date& operator++(int) //这里的加不加形参名都可以,必须要有int (只要整型){Date tmp;cout << "这里是后置++" << endl; return tmp;}private:int _year;int _month;int _day;
};int main()
{Date d1;Date d2;++d1;d2++;d2.operator++(10); //这里传不传值都可以,因为那个int一般不接收。
}
注意:重载的++运算符也要与内置类型++运算符规则一致(前置++:先++,再返回++后的结果,不产生拷贝;后置++:进行++,++之后返回的是++前的结果,会产生拷贝。所以,一般开以选择前置++来减少拷贝)。
我们实现日期类,还想需要可以对这个日期cout和cin来方便输出和输入,所以<<和>>也是可以重载的,但是需要重载成全局函数,重载为全局函数把ostream/istream放到第一个形参位置就可以了,第二个形参位置当类类型对象。否则会有隐含的this指针,导致调用时变成:对象<<cout ,不符合使用习惯和可读性(想要的是:cout<<对象)。
2.日期类实现代码
我们可以声明定义分离来实现,分别创建Date.h头文件和Date.cpp用来定义头文件声明的函数。
//Date.h#include<iostream>
#include <assert.h>
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);//拷贝构造Date(const Date& d);void Print()const; //打印日期bool CheckDate() const; //检查日期输入是否正确//频繁调用的建议直接定义类里面,默认inline内联int GetMonthDay(int year, int month)const {assert(month > 0 && month < 13);static int monthDay[13] = { -1,31,28,31,30,31,30,31,31,30,31,30,31 }; //因为没有0月,将0置-1空出来//用static修饰是因为这个数组会频繁调用,直接放在静态区if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))){return 29;}return monthDay[month];}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;//d1 = d2Date& operator=(const Date& d);Date operator+(int day) const;Date& operator+=(int day);Date operator-(int day) const;Date& operator-=(int day);// d1++;// d1.operator++(0);Date operator++(int);// ++d1;// d1.operator++();Date& operator++();// d1--;// d1.operator--(0);Date operator--(int);// --d1;// d1.operator--();Date& operator--();// d1 - d2int operator-(const Date& d) const;//void operator<<(ostream& out);Date* operator&() //取地址运算符重载 //普通对象返回类型Date*{return (Date*)0x2673FF40; //返回假地址}const Date* operator&() const //取地址运算符重载 //const对象要调用const成员函数要返回类型const Date*{return (Date*)0x2673FE30; //返回假地址 }
private:int _year;int _month;int _day;
};//重载流插入流提取
// ostream/istream类型对象不能传值只能传引用,否则会报错,因为c++不支持它们的拷贝构造。
//要写成全局函数 否则调用的时候需要这样写:d1<<cout d1>>cin 用着会比较别扭
//因为如果写成了成员函数第一个参数有隐藏的this指针,并且不能修改
//cout是ostream类型的 (也可以用void返回类型但不建议)
//cin默认关联cout
//在cin进行i/o操作前会刷新cout的缓冲区//流插入
ostream& operator<<(ostream& out, const Date& d);//流提取
//这里第一个参数不能加const修饰了,因为提取的值要放日期类对象里
istream& operator>>(istream& in, Date& d);
//Date.cpp#include "Date.h"
bool Date::CheckDate() const//检查日期正确
{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 << "非法日期:";Print();}cout << endl;
}Date::Date(const Date& d) //拷贝构造
{_year = d._year;_month = d._month;_day = d._day;
}void Date::Print()const //打印日期
{cout << _year << "/" << _month << "/" << _day << endl;
}//d1 = d2
Date& Date::operator=(const Date& d)
{if (*this != d){_year = d._year;_month = d._month;_day = d._day;}return *this;
}Date& Date::operator+=(int day) //加等可以让自己改变
{//先判断day是否是负数 ---- _day+(-day)==_day-dayif (day < 0){return *this -= (-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) const //只加不能改变自己
//这里不能用传引用返回 因为这里用const修饰只读取不想改变自己,引用会把权限放大
{Date tmp = *this;tmp += day;return tmp;
}//- -=同+ +=
Date Date::operator-(int day) const
{Date tmp = *this;tmp -= day;return tmp;
}
Date& Date::operator-=(int day)
{//先判断day是否是负数 --->_day-(-day)==_day+dayif (day < 0) {return *this += (-day);}_day -= day;while (_day <= 0){--_month;if (_month == 0){_month = 12;--_year;}_day += GetMonthDay(_year, _month);}return *this;
}//d1<d2
bool Date::operator<(const Date& d) const //这里传引用 d就是d2的别名 也就是d2的本身
{if (_year < d._year){return true;}else if (_year == d._year){if (_month < d._month){return true;}if (_month == d._month){return _day < d._day;}}return false;
}//d1<=d2
//这里就可以复用函数了
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);
}
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);
}// d1++; 后置++有拷贝
// d1.operator++(0);
//用的时候括号里只要是整数都可以
Date Date::operator++(int) // 加不加形参名都可以,一般不接收
{Date tmp = *this;*this += 1;return tmp;
}// ++d1; //前置++无拷贝
// d1.operator++();
Date& Date::operator++()
{*this += 1;return *this;
}//-- 同理++
// d1--;
// d1.operator--(0);
Date Date::operator--(int)
{Date tmp = *this;*this -= 1;return tmp;
}// --d1;
// d1.operator--();
Date& Date::operator--()
{*this -= 1;return *this;
}// d1 - d2
int Date::operator-(const Date& d) const
{Date max = *this; //假设第一个大Date min = d; //第二个小int flag = 1; //等于1时表示第一个大第二个小if (*this < d){max = d;min = *this;flag = -1; //第二个大第一个小}int n = 0;while (min != max){++min;++n;}return n *= flag;
}//流插入cout<<d1<<d2
ostream& operator<<(ostream& out, const Date& d)
{cout << d._year << "年" << d._month << "月" << d._day << "日" << endl;return out;
}//流提取cin>>d1>d2
istream& operator>>(istream& in, Date& d)
{cout << "请依次输入年月日:>";in >> d._year >> d._month >> d._day;//检查日期是否非法if (!d.CheckDate()){cout << "非法日期: " << d << "请重新输入" << endl;while (1){cout << "请依次输入年月日:>";in >> d._year >> d._month >> d._day;if (!d.CheckDate()){cout << "输入日期非法:";d.Print();cout << "请重新输入!!!" << endl;}else{break;}}}return in;
}
有些注意事项,在上面代码的实现注释中有一些解释。
制作不易,若有不足之处或出问题的地方,请各位大佬批评指正 ,感谢大家的阅读支持!!!
相关文章:

C++ —— 实现一个日期类
目录 一. 对日期类的介绍 二. 实现日期类 1. 运算符重载 2.日期类实现代码 一. 对日期类的介绍 通过对类和对象(这里链接是类和对象的介绍)的学习,类就是一种模型一样的东西,是对象一种抽象的描述。所以实现日期类࿰…...

Java全栈经典面试题剖析5】JavaSE高级 -- 集合
目录 面试题3.18 Java中有多少种数据结构,分别是什么? 面试题3.19 List、Set和Map的区别? 面试题3.20 List遍历方式有多少种 面试题3.21 Arraylist,Vector和Linkedlist 的区别 面试题3.22 Collection和Collections的区别 面试…...
python中如何获取对象信息
目录 一、获取对象类型 二、使用isinstance()函数 三、使用dir()函数 四、使用对象的__dict__属性(适用于大多数自定义对象) 五、使用文档字符串(__doc__属性)获取对象的文档信息 一、获取对象类型 使用type()函数ÿ…...
逐行讲解transformers中model.generate()源码
目录 简介输入程序model.generate()输入参数1. 创建生成参数对象 generation_config2. 初始化部分输入参数3. 定义模型输入4. 定义其他模型参数5. 准备 input_ids6. 准备 max_length7. 确定生成模式8. 准备 logits 处理器9. 准备 stopping 处理器10. 执行生成 self._sample1. 先…...
小白对时序数据库的理解
一、什么是时序数据库? 时序数据库(Time Series Database,TSDB)是一种专门用于存储、处理和分析时间序列数据的数据库管理系统。时间序列数据是按时间顺序记录的数据,通常由各种设备和传感器生成,例如智慧…...

打开游戏提示丢失(或找不到)XINPUT1_3.DLL的多种解决办法
xinput1_3.dll是一个动态链接库(DLL)文件,它在Windows操作系统中扮演着重要的角色。该文件作为系统库文件,通常存放于C:\Windows\System32目录下(对于32位系统)或C:\Windows\SysWOW64目录下(对于…...
netty的网络IO模型
参考: 聊聊Netty那些事儿之从内核角度看IO模型...

电子木鱼小游戏小程序源码系统 带完整的安装代码包以及搭建部署教程
系统概述 在快节奏的生活中,人们越来越注重内心的平静与放松。电子木鱼小游戏小程序正是基于这一需求而诞生的,它将传统的木鱼文化与现代科技相结合,为用户提供了一个简单、方便、有趣的冥想与放松工具。通过敲击屏幕上的虚拟木鱼࿰…...
支付域——交易系统设计
摘要 交易是业务流转的基础,其中交易系统和订单系统的设计至关重要。交易系统需确保安全、高效与稳定。在设计时,要考虑支付方式的多样性及兼容性,保障资金流转的准确与安全。同时,应具备良好的风险控制机制,防范欺诈等风险。订单系统则负责记录和管理交易的全过程。需清…...
IBus 和 Fcitx 框架下的rime输入法引擎
Rime 输入法引擎 Rime(中州韵输入法引擎):这是一个跨平台的输入法引擎,支持多种输入法方案,如拼音、五笔、注音等。Rime本身不提供前端界面,它需要与输入法框架(如IBus或Fcitx)结合…...

Java基础-JVM
JVM构成部分 类加载系统 类加载子系统的作用是将磁盘中的.class文件加载到内存当中。工作过程如下: 加载:通过类全路径名获取二进制字节流;将这个二进制字节流代表的数存储构转化为方法区运行时数据结构;在内存生成一个代表该类的…...
集成学习:投票法、提升法、袋装法
集成学习:投票法、提升法、袋装法 目录 🗳️ 投票法 (Voting)🚀 提升法 (Boosting)🛍️ 袋装法 (Bagging) 1. 🗳️ 投票法 (Voting) 投票法是一种强大的集成学习策略,它通过将多个模型的预测结果进行组合…...

波浪理论、江恩理论、价值投资的结合
结合波浪理论、江恩理论和价值投资,需要理解这三种方法的核心原理和应用方式。下面详细解析如何将它们融合在一起,形成一个更全面的投资策略: 1. 基本概述 波浪理论:由艾略特提出,通过分析市场波动的五个上升浪和三个…...

LRDDR4芯片学习(三)——命令和时序
ddr command: activate commandrefresh commandprecharge commandwrite/read commandburst write/read commandMRR/MRW command 一、Activate命令 在读写命令之前,必须要发送Activate命令,由ACTIVATE-1、ACTIVATE-2命令组成。ACTIVATE命令中包含了BA[…...
【趣学C语言和数据结构100例】
【趣学C语言和数据结构100例】 问题描述 61.假定采用带头结点的单链表保存单词,当两个单词有相同的后缀时,可共享相同的后缀存储空间,例如,loading 和 being 的存储映像如下图所示,设 strl 和 str2 分别指向两个单词所在单链表的头结点,链表结点结构为 data next。请设计…...

linux卸载数据库(最为完整的卸载方式)
1.首先检查是否安装了MySQL组件 我们可以看到有五个与mysql相关的组件 2.卸载前关闭MySQL服务 systemctl stop mysqld systemctl status mysqld 3.收集MySQL对应的文件夹信息 whereis mysql 4.卸载删除MySQL各类组件 #例如 rpm -ev --nodeps mysql-community-libs-5.7.…...

H7-TOOL的LUA小程序教程第15期:电压,电流,NTC热敏电阻以及4-20mA输入(2024-10-21,已经发布)
LUA脚本的好处是用户可以根据自己注册的一批API(当前TOOL已经提供了几百个函数供大家使用),实现各种小程序,不再限制Flash里面已经下载的程序,就跟手机安装APP差不多,所以在H7-TOOL里面被广泛使用ÿ…...
使用梧桐数据库进行销售趋势分析和预测
在当今竞争激烈的商业环境中,企业需要深入了解销售数据,以便做出明智的决策。销售趋势分析和预测是帮助企业把握市场动态、优化库存管理、制定营销策略的重要工具。本文将介绍如何使用SQL来创建销售数据库的表结构,插入示例数据,并…...

SQLITE排序
最终实现的效果:先查询第一层2列开始的1、4、2、3排,再查询第三列、四列...,然后第二层... 入库 排序优先级:层>列>排(1>2,4>3) 最终排的优先级 1>4>2>3 ORDER BY rack.rackLayer,rack.rackColumn, CASE rack.rackRowW…...
python的文件操作
文件操作 1.打开文件 2.读取文件内容 3.写入文件内容 4.关闭文件 要打开文件,可以使用open()函数并指定文件路径和模式。 file open("example.txt", "r") # 打开了一个名为"example.txt"的文件,并将其赋值给变量file。第…...

UE5 学习系列(二)用户操作界面及介绍
这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…...
day52 ResNet18 CBAM
在深度学习的旅程中,我们不断探索如何提升模型的性能。今天,我将分享我在 ResNet18 模型中插入 CBAM(Convolutional Block Attention Module)模块,并采用分阶段微调策略的实践过程。通过这个过程,我不仅提升…...
工程地质软件市场:发展现状、趋势与策略建议
一、引言 在工程建设领域,准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具,正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...
Neo4j 集群管理:原理、技术与最佳实践深度解析
Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)
宇树机器人多姿态起立控制强化学习框架论文解析 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一) 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...
相机Camera日志分析之三十一:高通Camx HAL十种流程基础分析关键字汇总(后续持续更新中)
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:有对最普通的场景进行各个日志注释讲解,但相机场景太多,日志差异也巨大。后面将展示各种场景下的日志。 通过notepad++打开场景下的日志,通过下列分类关键字搜索,即可清晰的分析不同场景的相机运行流程差异…...

技术栈RabbitMq的介绍和使用
目录 1. 什么是消息队列?2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...

【分享】推荐一些办公小工具
1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由:大部分的转换软件需要收费,要么功能不齐全,而开会员又用不了几次浪费钱,借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...
十九、【用户管理与权限 - 篇一】后端基础:用户列表与角色模型的初步构建
【用户管理与权限 - 篇一】后端基础:用户列表与角色模型的初步构建 前言准备工作第一部分:回顾 Django 内置的 `User` 模型第二部分:设计并创建 `Role` 和 `UserProfile` 模型第三部分:创建 Serializers第四部分:创建 ViewSets第五部分:注册 API 路由第六部分:后端初步测…...
文件上传漏洞防御全攻略
要全面防范文件上传漏洞,需构建多层防御体系,结合技术验证、存储隔离与权限控制: 🔒 一、基础防护层 前端校验(仅辅助) 通过JavaScript限制文件后缀名(白名单)和大小,提…...