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

基于面向对象设计的C++日期推算引擎:精准高效的时间运算实现与运算重载工程化实践

前引: 在软件开发中,时间与日期的处理是基础但极具挑战性的任务。传统的手工日期运算逻辑往往面临闰年规则、月份天数动态变化、时区转换等复杂场景的容错难题,且代码冗余度高、可维护性差。本文将深入探讨如何利用C++的面向对象特性与成员函数封装能力,构建一个高内聚、低耦合的日期推算系统。

 本文目的:深度 巩固+运用 C++运算符重载成员函数,细节夯实!

目录

实践引入

运算符重载实践—日期比大小

类的创建

日期比较

日期相等判断

 日期小于判断

日期小于等于判断

日期大于判断

日期大于等于判断

总结

运算符重载实践—日期推算

类的创建

日期推算(后)

计算当月天数

日期进位计算

测试

变式

 日期推算(前)

日期退位计算

测试 

日期推算(前置++)

测试

日期推算(前置--)

测试

 日期推算(后置--)

测试

 运算符重载实践—日期间隔


实践引入

我们日常生活中对于日期的使用很广泛,例如:距离高考***天、今天是*年*月*日.......

大家可以点击下面这个链接体验一下对于时间的计算:

https://onlinealarmkur.com/date/zh-cn/https://onlinealarmkur.com/date/zh-cn/

最近咱们学习的类和对象中 运算符重载成员函数 就可以来简单实现这个功能!

运算符重载实践—日期比大小

类的创建

既然是日期计算,那么还和上面的类一样,需要有年、月、日

class Timedate
{
public://构造函数Timedate(int year = 2025, int month = 5, int day = 9){_year = year;_month = month;_day = day;}private:int _year;int _month;int _day;
};
日期比较
日期相等判断

咱们得成员函数可以只在类里面进行声明,在另一个文件来完成函数的实现,需要注明函数来历!

//成员函数定义
bool Timedate::operator==(const Timedate St2)
{if (_year == St2._year && _month ==St2._month && _day == St2. _day){return true;}return false;
}

注意咱们的函数调用,以下两种调用方式是相等的(为了体验运算符重载的简洁性,选第二种)

St1 == St2;St1.operator==(St2);
 日期小于判断

按照上面的流程,我们先在成员函数里面声明

bool operator<(const Timedate St2);

然后跨文件实现该函数的定义

bool Timedate::operator<(const Timedate St2)
{if (_year < St2._year){return true;}if (_year == St2._year && _month < St2._month){return true;}if (_year == St2._year && _month == St2._month && _day < St2._day){return true;}return false;
}

日期小于等于判断

还是先写成员函数声明

bool operator<=(const Timedate St2);

随后重点来了!

第一种:我们可以和上面一样,通过 if 语句去判断来返回不同的值

第二种:咱们小于等于的判断不就是前面两个结合起来吗?所以我们可以调用上面两个函数,如下

bool Timedate::operator<=(const Timedate St2)
{return ((*this) < St2 || (*this) == St2);
}

 这里隐藏的 this 指针是指向 St1 的,这里其实跟函数调用一样,只是需要理解隐藏的 this 指针

St1 < St2;
(*this) < St2;
//二者等价
日期大于判断

有了前面的基础,我们同样有两种写法:

第一种:走 if 判断

第二种:“大于”条件不是刚好和“小于等于”相反吗!所以我们还是采用调用函数的方法来实现

bool operator>(const Timedate St2);
bool Timedate::operator>(const Timedate St2)
{return !((*this) < St2 || (*this) == St2);
}
日期大于等于判断

第一种:直接走 if 判断

第二种:调用运算符重载函数(大于等于与小于刚好互补)如下:

bool operator>=(const Timedate St2);
bool Timedate::operator>=(const Timedate St2)
{return !((*this) < St2);
}
总结

(1)运算符重载成员函数大大减少了代码量

(2)其次增加了代码表达的效果,对比之前的函数:需要有函数名、参数,而现在我们根据运算符就可以明了的判断出这个函数的功能,这是很直观的,较C语言一个大的进阶!

St1 == St2
St1 < St2
St1 <= St2
St1 > St2
St1 >= St2

运算符重载实践—日期推算

类的创建

 既然是日期计算,那么还和上面的类一样,需要有年、月、日

class Timedate
{
public://构造函数Timedate(int year = 2025, int month = 5, int day = 9){_year = year;_month = month;_day = day;}private:int _year;int _month;int _day;
};
日期推算(后)

功能:给一个天数,自当前日期开始,推算一定天数之后的日期

计算当月天数

功能:计算给定的这个月有多少天

因为会面临加法计算,对月或者年需要进位,所以我们需要知道当月的准确天数是多少

设计功能:

(1)可以通过 case 语句判断当月天数,最后判断是不是闰年,因为闰年的2月有29天

(2)通过 if 语句判断,根据给定的月直接确定天数,再走闰年的判断

//计算当月天数
int Timedate::Compute(int _year, int _month)
{//我们这里就拿case语句判断switch (_month){case 1:case 3:case 5:case 7:case 8:case 10:case 12:return 31;case 4:case 6:case 9:case 11:return 30;case 2:if (_year % 4 == 0 && _year % 100 != 0 || _year % 400 == 0){return 29;}elsereturn 28;}
}

下面我们来测试一下这个功能:

日期进位计算

我们只需要在一个循环对年、月进行进位计算,等循环结束再将剩余天数相加即可

不管咋样,我们直接加上天数,然后根据对应月的满天数去减即可

注意:我们是推算对应天数之后,所以不改变原来的日期,需要建立新对象

//推算进位
Timedate Timedate::operator+(int day)
{//既然是加,那么我们不能改变原来的日期,调用拷贝构造Timedate St3(*this);//对月进位St3._day += day;while (St3._day > Compute(St3._year,St3._month)){//日减少St3._day -= Compute(St3._year, St3._month);//月增加St3._month++;//如果超过12月,年进位,重置月份if (St3._month > 12){St3._year++;St3._month = 1;}}return St3;
}
测试

变式

上面是不改变原来的日期,如果我们现在需要改变呢?

很简单,我们只需要调用刚才的函数,再利用编译器默认的运算符重载函数赋值即可

函数声明:

Timedate& operator+=(int day);

函数实现:

Timedate& Timedate::operator+=(int day)
{return *this = ((*this) + 100);
}

然后利用编译器默认的运算符重载函数进行浅拷贝内置类型

这里补充一下:

因为我们没有写“等于”的运算符重载函数,编译器就会调用自己默认的运算符重载函数完成

(1)对内置类型发生浅拷贝

(2)自定义类型调用自己的重载函数(会直接更改地址) 

 日期推算(前)

有了上面的经验,日期推算前就是往前推一定天数之前的日期是多少

类的结构还是与上面的一样,下面我们来完成函数

日期退位计算
Timedate operator-(int day);

执行函数:

先更新月、再根据月判断年,最后更新天数

 
Timedate Timedate::operator-(int day)
{//先拷贝构造一个一模一样的对象,防止更改原来的日期Timedate St3(*this);//直接减去天数St3._day -= day;while (St3._day < 1){//小于1说明不符合规定//应该先减月St3._month--;//如果月小于1,说明要更新年了if (St3._month < 1){St3._year--;St3._month = 12;}//再加天数St3._day += Compute(St3._year, St3._month);}return St3;
}
测试 

日期推算(前置++)

前置加加是返回加完之后的结果,这点需要和后置加加区别。类的类型不变,我们直接进入函数

函数声明:

Timedate& operator++();

 函数实现:

Timedate& Timedate::operator++()
{//此时this指针传过来的就是调用函数的对象_day++;while (_day > Compute(_year, _month)){//如果超过当月天数_day -= Compute(_year, _month);//月加1_month++;//判断12月以上的情况if (_month > 12){_year++;_month = 1;}}return *this;
}

注意*this指向的对象是调用函数的对象,是全局生命域,所以可以使用引用

测试

日期推算(前置--)

函数声明:

Timedate& operator--();

函数实现:

Timedate& Timedate::operator--()
{_day --;//如果不满足月的要求,就退治while (_day < 1){_month--;if (_month < 1){_month = 12;_year--;}_day += Compute(_year, _month);}return *this;
}
测试

 日期推算(后置--)

后置唯一需要注意的是它的函数调用与前置减减达成重复,所以我们需要用一个参数进行区分

函数声明:

Timedate operator--(int);

函数实现:

Timedate Timedate::operator--(int)
{//拷贝构造Timedate St3(*this);_day--;while (_day < 1){_month--;if (_month < 1){_month = 12;_year--;}_day += Compute(_year, _month);}return St3;
}
测试

 运算符重载实践—日期间隔

有了上面的基础我们完成这个功能就很简单了!

第一种:最简单的多次调用“加加”函数,每调用一次,计数一次

第二种:分区段(第一次保证年相同,第二次再保证月、日相同)实现

第一种更加的直观,所以我们选择第一种!
函数声明:

int operator-(const Timedate St2);

函数实现:

(1)为了避免改变原对象,我们新建两个临时对象

(2)先假设其中一个对象较大,如果假设不成立,再将二者调换

(3)通过循环来多次调用“相等”运算重载函数,并且不断计数

(4)根据假设结果返回正负值,来直观的判断日期先后

int Timedate::operator-(const Timedate St2)
{//假设第一个参数更大Timedate St3 = (*this);Timedate St4 = St2;//用于返回正负值int flag = 1;//计数int date = 0;//调用运算重载函数确定大小if (St3 < St4){St3 = St4;St4 = *this;flag = -1;}//计算差值while (!( St3 == St4)){++St4;++date;}return flag * date;
}

                                                【雾非雾】期待与你的下次相遇! 

相关文章:

  • 【现代深度学习技术】注意力机制07:Transformer
  • 浅析 Spring 启动过程:从源码到核心方法
  • HPC软件使用之ANSYS Fluent
  • 欧拉角 Pitch Roll Yaw 学习笔记
  • RabbitMQ概述
  • 编程日志5.8
  • 如何通过partclone克隆Ubuntu 22系统
  • 【hot100-动态规划-139.单词拆分】
  • 使用 GitDiagram 快速将 GitHub 仓库转换为交互式图表
  • LeetCode 45. 跳跃游戏 II(中等)
  • 120页WORD方案 | 2025企业数字化转型AI大模型数字底座项目设计方案
  • 【LeetCode 热题 100】56. 合并区间 —— 一文弄懂排序+遍历经典解法(附Python代码)
  • IPLOOK | 2025 MVNOs 世界大会:从Wi-Fi通话到卫星覆盖
  • Python爬虫第21节- 基础图形验证码识别实战
  • 使用 `perf` 和火焰图(Flame Graph)进行性能分析
  • 随机森林(Random Forest)
  • 使用GmSSL v3.1.1实现SM2证书认证
  • 编译OpenSSL时报错,Can‘t locate IPC/Cmd.pm in @INC perl环境
  • VSCode CMake Debug
  • 蓝桥杯12届国B 完全日期
  • 政企共同发力:多地密集部署外贸企业抢抓90天政策窗口期
  • 澎湃·镜相第二届非虚构写作大赛初选入围名单公示
  • 北京13日冰雹过后,已受理各险种报案近3万件
  • 中哥两国元首共同见证签署《中华人民共和国政府与哥伦比亚共和国政府关于共同推进丝绸之路经济带和21世纪海上丝绸之路建设的合作规划》
  • 线下哪些商家支持无理由退货?查询方法公布
  • 缺字危机:一本书背后有多少“不存在”的汉字?