
基于面向对象设计的C++日期推算引擎:精准高效的时间运算实现与运算重载工程化实践
在软件开发中,时间与日期的处理是基础但极具挑战性的任务。传统的手工日期运算逻辑往往面临闰年规则、月份天数动态变化、时区转换等复杂场景的容错难题,且代码冗余度高、可维护性差。本文将深入探讨如何利用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;
}
else
return 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;
}
【雾非雾】期待与你的下次相遇!
更多推荐
所有评论(0)