C++自定义日期类的精彩之旅(详解)

        

        在学习了C++的6个默认成员函数后,我们现在动手实现一个完整的日期类,来加强对这6个默认成员函数的认识。
        这是日期类中所包含的成员函数和成员变量:

构造函数

// 函数:获取某年某月的天数
inline int GetMonthDay(int year, int month)
{// 静态数组,存储普通年份每个月的天数。注意:数组索引0未使用,实际月份从1开始static int dayArray[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };// 默认情况下,直接从数组中获取对应月份的天数int day = dayArray[month];// 特殊情况处理:如果是二月(即month == 2),并且year是闰年if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))){// 闰年的二月有29天day = 29;}// 返回计算得到的该月天数return day;
}// 类Date的构造函数,用于初始化一个日期对象
Date::Date(int year, int month, int day)
{// 检查传入的年、月、日是否构成一个合法的日期if (year >= 0    // 年份需为非负数&& month >= 1 && month <= 12  // 月份需在1-12之间&& day >= 1 && day <= GetMonthDay(year, month))  // 日需在1-该月最大天数之间{// 如果合法,则设置日期成员变量的值_year = year;_month = month;_day = day;}else{// 如果日期不合法,这里简单通过控制台输出错误信息// 更严谨的做法应该是抛出一个异常,让调用者来决定如何处理这个错误cout << "非法日期" << endl;cout << year << "年" << month << "月" << day << "日" << endl;}
}

GetMonthDay函数中的三个细节:

  • 该函数可能被多次调用,所以我们最好将其设置为内联函数。
  • 函数中存储每月天数的数组最好是用static修饰,存储在静态区,避免每次调用该函数都需要重新开辟数组。
  • 逻辑与应该先判断month == 2是否为真,因为当不是2月的时候我们不必判断是不是闰年。

注意:当函数声明和定义分开时,在声明时注明缺省参数,定义时不标出缺省参数。 

打印函数

// 打印函数
void Date::Print() const
{cout << _year << "年" << _month << "月" << _day << "日" << endl;
}

日期and天数

日期 += 天数

        对于+=运算符,我们先将需要加的天数加到日上面,然后判断日期是否合法,若不合法,则通过不断调整,直到日期合法为止。

调整日期的思路:

  • 若日已满,则日减去当前月的天数,月加一。
  • 若月已满,则将年加一,月置为1。
  • 反复执行1和2,直到日期合法为止。
// 重载运算符 '+=',使Date对象可以加上一个整数天数
Date& Date::operator+=(int day)
{// 如果要添加的天数是负数,转换成减法操作并调用operator-=,以复用已实现的逻辑if (day < 0){// 通过将负数转正并调用减法操作符来实现减去天数的功能*this -= -day; }else{// 直接将天数加到当前日期的天数上_day += day;// 确保累加后的日期是合法的,如果不是,则逐步调整至合法日期while (_day > GetMonthDay(_year, _month)){// 若累加后超过当月天数,从下个月的天数中继续累加_day -= GetMonthDay(_year, _month);// 进入下一个月_month++;// 如果月份超过12,则年份增加,并将月份重置为1if (_month > 12){_year++;_month = 1;}}}// 返回当前对象的引用,支持链式赋值return *this;
}

 注意:当需要加的天数为负数时,转而调用-=运算符重载函数。

日期 + 天数

        +运算符的重载,我们可以复用上面已经实现的+=运算符的重载函数。

        但要注意:虽然我们返回的是加了之后的值,但是对象本身的值并没有改变。就像a = b + 1,b + 1的返回值是b + 1,但是b的值并没有改变。所以我们还可以用const对该函数进行修饰,防止函数内部改变了this指针指向的对象。

// 重载运算符 '+',允许Date对象与一个表示天数的整数相加,生成一个新的Date对象
Date Date::operator+(int day) const
{// 创建当前日期对象的一个副本tmp,避免修改原始对象的值Date tmp(*this); // 使用拷贝构造函数创建副本// 复用已实现的operator+=方法,将天数加到tmp上tmp += day; // 返回累加天数后的新日期对象return tmp;
}

注意:+=运算符的重载函数采用的是引用返回,因为出了函数作用域,this指针指向的对象没有被销毁。但+运算符的重载函数的返回值只能是传值返回,因为出了函数作用域,对象tmp就被销毁了,不能使用引用返回。 

日期 -= 天数

        对于-=运算符,我们先用日减去需要减的天数,然后判断日期是否合法,若不合法,则通过不断调整,直到日期合法为止。

调整日期的思路:

  • 若日为负数,则月减一。
  • 若月为0,则年减一,月置为12。
  • 日加上当前月的天数。
  • 反复执行1、2和3,直到日期合法为止。
// 重载运算符 '-=', 允许Date对象减去一个表示天数的整数,并直接修改当前对象的日期
Date& Date::operator-=(int day)
{if (day < 0){// 如果要减去的天数是负数,则转换为加上一个正数天数,复用operator+=方法*this += -day; }else{// 直接减去天数_day -= day;// 确保日期合法性:如果_day小于等于0,需要向前调整月份乃至年份while (_day <= 0){// 减少月份,若月份变为0,则同时减少年份并设置月份为12(上年的12月)_month--;if (_month == 0){_year--;_month = 12;}// 根据调整后的年月获取该月的实际天数,并累加到_day中,直至_day为正数,确保日期有效_day += GetMonthDay(_year, _month);}}// 返回当前对象的引用,以便支持链式赋值return *this;
}

 注意:当需要减的天数为负数时,转而调用+=运算符重载函数。

日期 - 天数

        和+运算符的重载类似,我们可以复用上面已经实现的-=运算符的重载函数,而且最好用const对该函数进行修饰,防止函数内部改变了this指针指向的对象。

// 重载运算符 '-',允许Date对象减去一个表示天数的整数,并返回一个新的Date对象表示结果
Date Date::operator-(int day) const
{// 创建当前日期对象的一个副本tmp,以避免修改原始对象Date tmp(*this); // 利用拷贝构造函数创建副本// 复用已实现的operator-=方法,从副本tmp中减去指定的天数tmp -= day; // 返回减去天数后的新日期对象return tmp;
}

注意:-=运算符的重载函数采用的是引用返回,但-运算符的重载函数的返回值只能是传值返回,也是由于-运算符重载函数中的tmp对象出了函数作用域被销毁了,所以不能使用引用返回。 

前置and后置

前置 ++

        前置++,我们可以复用+=运算符的重载函数。

// 重载前置自增运算符 '++',使Date对象自身向前推进一天并返回修改后的对象
Date& Date::operator++()
{// 直接复用已实现的operator+=方法,将当前日期对象增加一天*this += 1; // 返回经过自增操作后的当前对象(即指向自身的引用)return *this;
}

后置 ++

        由于前置++和后置++的运算符均为++,为了区分它们的运算符重载,我们给后置++的运算符重载的参数加上一个int型参数,使用后置++时不需要给这个int参数传入实参,因为这里int参数的作用只是为了跟前置++构成重载。

// 重载后置自增运算符 '++',使Date对象自身向前推进一天,并返回自增前的日期对象
Date Date::operator++(int)
{// 创建当前日期对象的一个副本tmp,用于保存自增前的日期Date tmp(*this); // 使用拷贝构造函数创建副本// 复用已实现的operator+=方法,将当前日期对象增加一天*this += 1; // 返回自增操作前的日期对象tmpreturn tmp;
}

注意:后置++也是需要返回加了之前的值,只能先用对象tmp保存之前的值,然后再然对象加一,最后返回tmp对象。由于tmp对象出了该函数作用域就被销毁了,所以后置++只能使用传值返回,而前置++可以使用引用返回。 

前置 --

        复用前面的-=运算符的重载函数。

// 重载前置自减运算符 '--',使Date对象自身向后回退一天并返回修改后的对象
Date& Date::operator--()
{// 直接复用已实现的operator-=方法,将当前日期对象减去一天(即向前一天)*this -= 1; // 返回经过自减操作后的当前对象(即指向自身的引用)return *this;
}

后置--

// 重载后置自减运算符 '--',使Date对象自身向后回退一天,并返回回退前的日期对象
Date Date::operator--(int)
{// 创建当前日期对象的一个副本tmp,用来保存自减操作前的日期Date tmp(*this); // 利用拷贝构造函数创建副本// 复用已实现的operator-=方法,将当前日期对象减去一天(即向后一天)*this -= 1; // 返回自减操作执行前的日期对象tmpreturn tmp;
}

日期类的大小关系比较

        日期类的大小关系比较需要重载的运算符看起来有6个,实际上我们只用实现两个就可以了,然后其他的通过复用这两个就可以实现。

注意:进行日期的大小比较,我们并不会改变传入对象的值,所以这6个运算符重载函数都应该被const所修饰。

>运算符的重载

        >运算符的重载很简单,先判断年是否大于,再判断月是否大于,最后判断日是否大于,这其中有一者为真则函数返回true,否则返回false。

// 重载大于比较运算符 '>', 用于比较两个Date对象的大小
bool Date::operator>(const Date& d) const
{// 首先比较年份,如果当前对象的年份大于参数对象的年份,则当前对象更大,返回trueif (_year > d._year){return true;}// 如果年份相同,则继续比较月份else if (_year == d._year){// 当前对象的月份大于参数对象的月份,则当前对象更大,返回trueif (_month > d._month){return true;}// 如果月份也相同,则继续比较日else if (_month == d._month){// 当前对象的日大于参数对象的日,则当前对象更大,返回trueif (_day > d._day){return true;}}}// 如果以上条件都不满足,说明当前对象不大于参数对象,返回falsereturn false;
}

==运算符的重载

        ==运算符的重载也是很简单,年月日均相等,则为真。

// 重载等于比较运算符 '==', 用于判断两个Date对象是否表示相同的日期
bool Date::operator==(const Date& d) const
{// 同时检查年、月、日是否分别相等return (_year == d._year)          // 年份相等&& (_month == d._month)     // 月份相等&& (_day == d._day);       // 日相等// 所有条件都满足时,认为两个Date对象表示相同的日期,返回true// 否则,返回false
}

>=运算符的重载

// 重载大于等于比较运算符 '>=', 判断当前Date对象是否大于或等于另一个Date对象
bool Date::operator>=(const Date& d) const
{// 使用逻辑或运算符检查当前对象是否大于(使用已重载的>运算符)或等于(使用已重载的==运算符)参数对象dreturn (*this > d) || (*this == d);// 如果任何一个条件为真(即当前对象大于d,或者等于d),则返回true,表示当前对象大于等于d// 否则,返回false
}

<运算符的重载

// 重载小于比较运算符 '<', 判断当前Date对象是否小于另一个Date对象
bool Date::operator<(const Date& d) const
{// 通过逻辑非操作符'!'来反转大于等于运算的结果,从而判断当前对象是否小于参数对象dreturn !(*this >= d);// 如果当前对象不大于等于d(即不等于d也不大于d),则返回true,表示当前对象小于d// 否则,返回false
}

<=运算符的重载

// 重载小于等于比较运算符 '<=', 判断当前Date对象是否小于或等于另一个Date对象
bool Date::operator<=(const Date& d) const
{// 通过逻辑非操作符'!'来反转大于运算的结果,从而判断当前对象是否小于或等于参数对象dreturn !(*this > d);// 如果当前对象不大于d(即小于d或等于d),则返回true,表示当前对象小于等于d// 否则,返回false
}

!=运算符的重载

// 重载不等于比较运算符 '!=', 判断当前Date对象是否与另一个Date对象表示不同的日期
bool Date::operator!=(const Date& d) const
{// 通过逻辑非操作符'!'来反转等于运算的结果,从而判断当前对象是否不等于参数对象dreturn !(*this == d);// 如果当前对象不等于d(即年、月、日中有任一不同),则返回true,表示两个日期不同// 否则,返回false 表示两个日期相同
}

日期 - 日期

        日期 - 日期,即计算传入的两个日期相差的天数。我们只需要让较小的日期的天数一直加一,直到最后和较大的日期相等即可,这个过程中较小日期所加的总天数便是这两个日期之间差值的绝对值。若是第一个日期大于第二个日期,则返回这个差值的正值,若第一个日期小于第二个日期,则返回这个差值的负值。

// 重载减法运算符 '-', 用于计算两个Date对象之间的天数差
int Date::operator-(const Date& d) const
{// 初始化两个临时Date对象,假设*this为较大日期(max),d为较小日期(min)Date max = *this;Date min = d;// 初始化标记变量flag为1,表示差值预期为正int flag = 1;// 如果假设错误,即*this实际上小于d,则交换max和min,并将flag设为-1以表示差值应为负if (*this < d){max = d;min = *this;flag = -1;}// 初始化计数器n用于记录累加的总天数int n = 0;// 当较小的日期(min)不等于较大的日期(max)时,持续累加天数while (min != max){// 将较小的日期增加一天min++; // 总天数累加n++;}// 最终返回n乘以flag,以确保正确的正负符号,即两个日期之间的实际天数差return n * flag;
}

        代码中使用flag变量标记返回值的正负,flag为1代表返回的是正值,flag为-1代表返回的是负值,最后返回总天数与flag相乘之后的值即可。

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

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

相关文章

常见磁盘分区问题

给磁盘分区有几个主要的原因&#xff1a; 组织和管理数据&#xff1a;分区可以帮助用户更好地组织和管理数据。例如&#xff0c;你可以在一个分区上安装操作系统&#xff0c;而在另一个分区上存储个人文件。这样&#xff0c;即使操作系统崩溃或需要重新安装&#xff0c;你的个…

Docker 使用 Fedora 镜像

Fedora 在 Docker 中的使用也非常简单&#xff0c;直接使用命令 docker run -it fedora:latest bash 就可以 pull 到本地的容器中并且运行。 C:\Users\yhu>docker run -it fedora:latest bash Unable to find image fedora:latest locally latest: Pulling from library/fed…

【瑞萨RA6M3】2. UART 实验

https://blog.csdn.net/qq_35181236/article/details/132789258 使用 uart9 配置 打印 void hal_entry(void) {/* TODO: add your own code here */fsp_err_t err;uint8_t c;/* 配置串口 */err g_uart9.p_api->open(g_uart9.p_ctrl, g_uart9.p_cfg);while (1){g_uart9.…

HQChart实战教程72-美股盘前,盘中,盘后分时图

HQChart实战教程72-美股盘前,盘中,盘后分时图 美股交易时间段HQChart效果盘前盘中盘后全部HQChart插件地址实现步骤1. 股票代码增加2级后缀2. 创建交易时间段4. 重载分时图X轴刻度完整示例代码美股交易时间段 在美国东部时间上午9:30至下午4:00的正常交易时间之外,投资者可以…

前端起dev从110秒减少到7秒, 开发体验大幅提升

[webpack由浅入深]系列的内容 第一层: 了解一个小功能的完整流程. 看完可以满足好奇心和应付原理级别面试.第二层: 源码陪读, webpack源码比较灵活, 自己看容易陷入迷惑. 文章里会贴出关键流程的代码来辅助阅读源码. 如果你正在自己调试, 在这些方法上下断点会节约你宝贵的时间…

Java 语言的“编译与解释并存”

Java 语言被称为“编译与解释并存”&#xff0c;是因为它结合了编译型语言和解释型语言的特点&#xff0c;具有独特的运行机制。这个特点是由 Java 的编译器和 Java 虚拟机 (JVM) 共同实现的。下面我们详细剖析这一过程&#xff0c;并通过具体示例进行说明。 编译与解释的过程…

2024护网蓝队面试题

2024护网蓝队面试题 一. 目前有防火墙&#xff0c;全流量检测&#xff0c;态势感知&#xff0c;IDS&#xff0c;waf&#xff0c;web服务器等设备&#xff0c;如何搭建一个安全的内网环境&#xff0c;请给出大概拓扑结构 &#xff08;适用于中高级&#xff09; 1.1 全流量与态…

根据ip限制接口访问次数

前言 我们利用redis去实现这个功能&#xff0c;redis的天然高并发和内存单线程速度拉满&#xff0c;非常适合做这个场景。为了可用性&#xff0c;我们把它封装成注解形式&#xff0c;哪个接口想被根据ip限制接口访问次数&#xff0c;直接标注上注解即可。 一、添加配置 在yaml…

mysql的隔离性——MVCC

MVCC通过undolog版本链和readview来实现 更新和删除时会写入undolog中。 读已提交&#xff1a;在事务任意读时创建readview&#xff0c;读最新提交的事务 可重复读&#xff1a;在事务第一次读时创建readview

【opencv】图像畸变校正

接上篇文章&#xff1a;【鱼眼&#xff0b;普通相机】相机标定 附代码&#xff1a; 方法一&#xff1a; 使用cv2.undistort """Create May 11, 2024author Wang Jiajun """import cv2 import numpy as npdef correct(img,camera_fileE:/cali…

使用Caché管理工具

Cach通过一个web工具来对其进行系统管理和完成管理任务,该方法的一个好处是不必将Cach安装到用于管理的系统上。目前,通过网络远程管理和控制对站点的访问,这些都比较容易。因为数据及其格式信息都直接来自被管理的系统,因此,这也可以最小化跨版本的兼容问题。 本文将描述…

lua面向对象

建议提前学习https://www.runoob.com/lua/lua-metatables.html 面向对象特征 1&#xff09; 封装&#xff1a;指能够把一个实体的信息、功能、响应都装入一个单独的对象中的特性。2&#xff09; 继承&#xff1a;继承的方法允许在不改动原程序的基础上对其进行扩充&#xff0…

图的深度优先遍历

way&#xff1a;栈&#xff0c;map&#xff08;或set&#xff0c;只是我想用map&#xff09;记录是否访问过&#xff0c;放入时记录为已访问&#xff0c;打印&#xff0c;邻接的没访问过先入cur&#xff0c;再入邻接的节点&#xff0c;放入一个邻接的节点后及时break去下一个深…

Kubernetes二进制(单master)部署

文章目录 Kubernetes二进制&#xff08;单master&#xff09;部署一、常见的K8S部署方式1. Minikube2. Kubeadmin3. 二进制安装部署4. 小结 二、K8S单&#xff08;Master&#xff09;节点二进制部署1. 环境准备1.1 服务器配置1.2 关闭防火墙1.3 修改主机名1.4 关闭swap1.5 在/e…

(done) 关于 pytorch 代码里常出现的 batch_first 到底是啥?

参考文章&#xff1a;https://pytorch.org/docs/stable/generated/torch.nn.utils.rnn.pad_sequence.html 首先看参考文章里的解释&#xff0c;如下图 从文章描述来看&#xff0c;当 batch_first True 时&#xff0c;输出的张量的 size 是 B x T x *。当 batch_first False…

umi搭建react项目

UMI 是一个基于 React 的可扩展企业级前端应用框架&#xff0c;提供路由、状态管理、构建和部署等功能&#xff0c;可以帮助开发者快速构建复杂的单页面应用&#xff08;SPA&#xff09;和多页面应用&#xff08;MPA&#xff09;。它与 React 的关系是&#xff0c;UMI 构建在 R…

0.0和0.00竟然不相等!!!BigDecimal别用错了比较方式

对于BigDecimal字段&#xff0c;可以使用compareTo()方法和equals()方法进行比较。但是要注意这两种方法的作用有所不同。一般都应该使用BigDecimal比较值&#xff0c;而不是使用经常用到的equals方法比较内容。 1.compareTo()方法 是用来比较两个BigDecimal对象的大小关系。…

出现dependencies.dependency.version‘ for xxxx:jar is missing的解决方法

目录 1. 问题所示2. 原理分析3. 解决方法1. 问题所示 出现如下问题:dependencies.dependency.version for xxxx:jar is missing. 且一直提示Pom文件缺失依赖包(由于公司项目,此处不放图) 2. 原理分析 这个错误通常发生在 Maven 项目中,表示在项目的依赖关系中找不到指定…

大数据知识点分享:Python的固定语法

Python编码声明 为源文件指定特定的字符编码&#xff0c;需要在py文件的首行或第二行插入一行特殊的注释行 #-*-coding:utf-8-*- 2.单行注释 单行注释以井号&#xff08;#&#xff09;开头 # 这是一个单独成行的注释 print(Hello, World!) # 这是一个在代码后面的注释 3…