C++模拟实现-----日期计算器(超详细解析,小白一看就会!)
目录
一、前言
二、日期类计算器
三、日期计算器的实现
🍎日期计算器各个接口的实现
🍐日期计算器的需求
🍉打印当前日期(并检查日期是否合理)
💦检查日期是否合理
💦日期类构造函数(用于日期的初始化)
⭐效果展示
🍓日期之间的大小比较
💦< 运算符重载
💦== 运算符重载
💦<= 运算符重载
💦> 运算符重载
💦>= 运算符重载
💦!= 运算符重载
⭐效果展示
🍌日期的加减天数计算
💦日期 + 天数
💦日期 + = 天数
💦 日期 -= 天数
💦日期 - 天数
💦日期 前置++
💦日期 后置++
💦日期 前置--
💦日期 后置--
⭐效果展示
🍊日期与日期之间的减法运算
💦日期 - 日期
⭐效果展示
四、日期计算器的完整代码
🍇Date.h
🍋Date.cpp
🥝Test.cpp
🍍代码运行的界面
五、共勉
一、前言
在之前的博客学习中,我们已经详细的讲解了C++中的引用、缺省函数、this指针、构造函数、析构函数、拷贝构造函数、运算符重载等非常重要的知识,但是对于这些知识如何如何的使用还没有进行讲解,所以本次博客将以日期计算器为例,将以上知识融合起来讲解,帮助大家更好的理解。
二、日期类计算器
在我们的日常生活中,我们可能需要计算几天后的日期,或计算日期差等,现如今计算日期的方式有很多,简单粗暴的直接查看日历,快捷点的直接使用日期计算器来求得,先给一个网络上的日期计算器截图:
现在,就让我们用代码来实现其工作原理吧。
三、日期计算器的实现
🍎日期计算器各个接口的实现
这里先建立三个文件:
1️⃣ :Date.h文件,用于类中---函数声明
2️⃣ :Date.cpp文件,用于类中---函数的定义
3️⃣ :Test.cpp文件,用于测试函数
建立三个文件的目的: 将日期计算器作为一个项目来进行书写,方便我们的学习与观察。
🍐日期计算器的需求
这里我们需要考虑,我们实现的这个日期计算器,需要实现怎样的需求呢?
- 需要打印当前的日期(请检查日期是否合理)
- 实现日期之间的大小比较
- 实现日期的加减天数计算
- 实现日期与日期之间的减法运算
🍉打印当前日期(并检查日期是否合理)
实现日期类首先就得检查日期的合法性,这其中就包括大小月,闰年的2月有29天,一年只有12个月等等细节都要考虑到。
💦检查日期是否合理
//判断是否为闰年 bool Date::isLeaveYear(int year) { // (四年一润,百年不润) 或者 四百年一润 return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0); } // 获取每个月的天数 int Date::GetMonthDay(int year, int month) { int monthday[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 }; if (month == 2 && isLeaveYear(year)) { return 29; } else { return monthday[month]; } }
💦日期类构造函数(用于日期的初始化)
//判断是否为闰年 bool Date::isLeaveYear(int year) { // (四年一润,百年不润) 或者 四百年一润 return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0); } // 获取每个月的天数 int Date::GetMonthDay(int year, int month) { int monthday[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 }; if (month == 2 && isLeaveYear(year)) { return 29; } else { return monthday[month]; } }
⭐效果展示
1️⃣:错误的日期演示 ------ -1 / -1 / -1 (年/月/日)
// 测试初始化 void Test() { Date d1(-1,-1,-1); d1.Printf(); } int main() { Test(); return 0; }
1️⃣:正确的日期演示 -------- 2023 / 10 / 28 (年/月/日)// 测试初始化 void Test() { cout << "请输入今日的日期" << endl; Date d1(2023,10,28); d1.Printf(); } int main() { Test(); return 0; }
🍓日期之间的大小比较
💦< 运算符重载
- 思路:
< 运算符重载在我上一篇博文已经详细讲解过,主要是先把大于的情况全部统计出来,就比如我要比较实例化对象d1是否小于实例化对象d2,只需考虑如下三种满足的情况:
- d1的年小于d2的年
- d1与d2年相等,d1的月小于d2的月
- d1与d2年相等月相等,d1的天小于d2的天
这三种全是小于的情况,返回true,其余返回false
- 代码如下:
// "<" 运算符重载 bool Date::operator<(const Date& d) { if (_year < d._year || _year == d._year && _month < d._month || _year == d._year && _month == d._month && _day < d._day) { return true; } else { return false; } }
💦== 运算符重载
- 思路:
== 运算符重载其实非常简单,只需要判断d1和d2的年、月、天是否对应相等即可:
- 代码如下:
// "==" 运算符重载 bool Date::operator==(const Date& d) { return _year == d._year && _month == d._month && _day == d._day; }
💦<= 运算符重载
- 思路: -- 复用 (这里复用的意思是:用之前写好的运算符重载,可以减少我们的代码量)
<= 的运算符重载,这里要仔细想一想 <= 成立的条件是啥。不就是 要么 < 要么 = 吗?我们只需要复用先前写的 < 运算符重载和 <=运算符重载,无需自己费老大劲推导其内部原理。
- 代码如下:
// "<=" 运算符重载 bool Date::operator<=(const Date& d) { // 运算符重载的复用 return *this < d || *this == d; }
💦> 运算符重载
- 思路: -- 复用
> 的反义就是 <=,所以我们只需要复用 <= 运算符重载,再对其取反即可解决此问题。
- 代码如下:
// ">" 运算符重载 bool Date::operator>(const Date& d) { return !(*this <= d); }
💦>= 运算符重载
- 思路: -- 复用
>= 的反义就是 <,所以我们只需要复用 < 运算符重载,再对其取反即可。
- 代码如下:
// ">="运算符重载、 bool Date::operator>=(const Date& d) { return !(*this < d); }
💦!= 运算符重载
- 思路: -- 复用
有了前面的基础,写个 != 也很简单,对 == 取反即可
- 代码如下:
//"!="运算符重载 bool Date::operator!=(const Date& d) { return !(*this == d); }
⭐效果展示
此时我们拿 "<"运算符 举例子
// 测试"<"运算符 void Test1() { Date d1(2023,10,28); cout << "d1的日期为:"; d1.Printf(); cout << "d2的日期为:"; Date d2(2023, 10, 27); d2.Printf(); cout << endl; if (d2 < d1) { cout << "d1的日期 < d2的日期" << endl; } else { cout << "d2的日期 > d1的日期" << endl; } } int main() { Test1(); return 0; }
🍌日期的加减天数计算
💦日期 + 天数
- 思路:
对于日期 + 天数,我们得到的还是一个日期。特别需要注意进位的问题(天满了往月进,月满了往年进),主要考虑如下几个特殊点:
- 加过的天数超过该月的最大天数,需要进位
- 当月进位到13时,年进位+1,月置为1
- 法一:
Date Date::operator+(int day) { Date ret(*this); //拷贝构造,拿d1去初始化ret ret._day += day; while (ret._day > GetMonthDay(ret._year, ret._month)) { ret._day -= GetMonthDay(ret._year, ret._month); ret._month++; if (ret._month == 13) { ret._year++; ret._month = 1; } } return ret; }
出了作用域,对象ret不在,它是一个局部对象,我们这里不能用引用,用了的话,返回的就是ret的别名,但是ret又已经销毁了,访问野指针了,所以出了作用域,如果对象不在了,就不能用引用返回,要用传值返回
- 法二:复用日期+=天数
此法是建立在日期+=天数的基础上完成的,这里各位可以先看下文日期+=天数,然后我们进行复用:
Date Date::operator+(int day) { //法二:复用日期 += 天数 Date ret(*this); ret += day; return ret; }
- 法一和法二熟优?
答案:法二更好,也就是用+去复用+=,具体原因在下文会解释。
💦日期 + = 天数
这里实现 += 其实有两种方案
- 法一:
前面我实现的日期+天数,仔细观察我的代码,函数的第一行,我就调用了一个拷贝构造:
Date ret(*this); //拷贝构造,拿d1去初始化ret
这里调用拷贝构造,是为了不在*this本身上做变动,只在ret上进行操作,其理由是日期+天数得到的是另一个日期,而不用拷贝构造直接在*this上做改动只会导致原有的日期也变化,而这个变化正是我日期 += 天数的需求
仔细想想:+=天数就是在原有的日期上再加一定的天数,直接对*this做手脚即可,因此只需对日期+天数的代码进行小改动即可:
Date& Date::operator+=(int day) //传引用返回 { //如果day小于0,要单独处理 if (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; }
注意这里是传引用返回,原因就在于我返回的*this是全局的,出了作用域还在
- 法二:复用日期 +天数
Date& Date::operator+=(int day) { //法二:复用 * this = *this + day; //让d1+过天数后再返回给自己从而实现+= return *this; }
- 法一和法二熟优?
答案:法一。其实讨论这个问题就是在讨论用+去复用+=好还是用+=复用+好,答案是用+=去复用+好,因为+有两次拷贝,而+=没有拷贝,所以实现+,并且用+=去复用+效率更高
💦 日期 -= 天数
- 思路:
日期-=天数得到的还是一个日期,且是在原日期的基础上做改动。合法的日期减去天数后的day只要>0就没问题,若小于0就要借位了。要注意当减去的天数<0时单独讨论。具体步骤如下:
- 当减的天数为负数,则为+=,直接调用
- 若减后的day<0,月-1
- 若月 = 0,则年-1,月置为12
- 代码如下:
// 日期-= 天数 :d1 d1 - 100 Date& Date::operator-=(int day) { if (day < 0) { return *this += (-day); } _day -= day; while (_day <= 0) { _month--; if (_month == 0) { _month = 12; _year--; } _day += GetMonthDay(_year, _month); } return *this; }
💦日期 - 天数
有了先前日期+和+=的基础,这里实现日期 - 天数直接复用日期 -= 天数即可:
// 日期-天数 : d1 - 100 Date Date::operator-(int day) { // 调用拷贝构造 Date temp(*this); // 复用 "-=" temp -= day; return temp; }
💦日期 前置++
- 思路:
C++里有前置++和后置++,这就导致一个巨大的问题,该如何区分它们,具体实现过程不难(直接复用+=即可),难的是如何区分前置和后置。因此C++规定,无参的为前置,有参的为后置。
- 代码如下:
// 前置++ (无参的为前置,有参的为后置) Date& Date::operator++() { // 直接复用 += *this += 1; return *this; }
💦日期 后置++
- 思路:
有参的即为后置,后置++拿到的返回值应该是自己本身未加过的,因此要先把自己保存起来,再++*this,随后返回自己。
- 代码如下:
// 后置++(无参的为前置,有参的为后置) // int i 这里的形参可以写,可以不写 Date Date::operator++(int i) { Date temp(*this); *this += 1; return temp; }
💦日期 前置--
- 思路:
前置--和前置++没啥区别,只不过内部复用的是-=
- 代码如下:
// 前置-- Date& Date::operator--() { *this -= 1; return *this; }
💦日期 后置--
- 思路:
后置--和后置++类似,只不过内部复用的是-=,不再赘述
- 代码如下:
//后置-- Date Date::operator--(int i) { Date temp(*this); *this -= 1; return temp; }
⭐效果展示
举例:将日期 + 100 天 :2023 / 10 / 28 + 100
// d1+=100 测试 void Test3() { Date d1(2023, 10, 28); cout << "d1的日期为:"; d1.Printf(); cout << endl; d1 += 100; cout << "d1的日期+100为:"; d1.Printf(); } int main() { Test3(); return 0; }
🍊日期与日期之间的减法运算
💦日期 - 日期
- 思路:
日期 - 日期得到的是天数,首先我们得判断两个日期的大小,用min和max代替小的和大的,随后,算出min和max之间的差距,若min!=max,则min就++,随即定义变量n也自增++,最后返回n(注意符号)
- 代码如下:
// 日期 - 日期 int Date::operator-(const Date& d) { // 方便后续计算正负 int flag = 1; Date max = *this; Date min = d; // 确保max是大的 min是小的 if (*this < d) { min = *this; max = d; flag = -1; //计算正负 } int n = 0; // 计算min和max之间的绝对值差距 while (min != max) { min++; n++; } return n * flag; }
⭐效果展示
举例:计算 2023 / 10 /28 ------------- 2023 / 11 / 11 的天数差距
// 日期 - 日期 void Test4() { Date d1(2023, 11, 11); cout << "d1的日期为:"; d1.Printf(); cout << "d2的日期为:"; Date d2(2023, 10, 28); d2.Printf(); cout << endl; cout << "d1与d2之间的天数差距为:"; int ret = d1 - d2; cout << ret << endl; } int main() { Test4(); return 0; }
四、日期计算器的完整代码
🍇Date.h
#pragma once #include <iostream> #include <stdio.h> #include <assert.h> #include <stdbool.h> using std::cout; using std::cin; using std::endl; class Date { public: // 构造函数用于初始化 -- 声明 // 在声明中缺省函数需要 写清楚 在定义中缺省函数就不要写了 防止编译器分不清 Date(int year = 1, int month = 1, int day = 1); //判断是否为闰年 bool isLeaveYear(int year); // 获取每个月的天数 int GetMonthDay(int year, int month); // 打印 void Printf(); // "<" 运算符重载 bool operator<(const Date& d); // "==" 运算符重载 bool operator==(const Date& d); // "<=" 运算符重载 bool operator<=(const Date& d); // ">" 运算符重载 bool operator>(const Date& d); // ">="运算符重载、 bool operator>=(const Date& d); //"!="运算符重载 bool operator!=(const Date& d); // 日期+= 天数: d1 = d1 + 100 Date& operator+= (int day); // 日期 + 天数 d1+100 d1不变 Date operator+(int day); // 日期-= 天数 :d1 = d1 - 100 Date& operator-=(int day); // 日期-天数 : d1 - 100 Date operator-(int day); // 前置++ (无参的为前置,有参的为后置) Date& operator++(); // 后置++(无参的为前置,有参的为后置) Date operator++(int i); // 前置-- Date& operator--(); //后置-- Date operator--(int i); // 日期 - 日期 int operator-(const Date& d); private: int _year; int _month; int _day; };
🍋Date.cpp
#define _CRT_SECURE_NO_WARNINGS 1 #include "Date.h" // 构造函数用于初始化 Date::Date(int year, int month, int day) { _year = year; _month = month; _day = day; if (_year < 1 || _month < 1 || _month>12 || _day<1 || _day>GetMonthDay(_year, _month)) { assert(false); } } //判断是否为闰年 bool Date::isLeaveYear(int year) { // (四年一润,百年不润) 或者 四百年一润 return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0); } // 获取每个月的天数 int Date::GetMonthDay(int year, int month) { int monthday[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 }; if (month == 2 && isLeaveYear(year)) { return 29; } else { return monthday[month]; } } void Date::Printf() { cout << _year << " / " << _month << " / " << _day << endl; } // "<" 运算符重载 bool Date::operator<(const Date& d) { if (_year < d._year || _year == d._year && _month < d._month || _year == d._year && _month == d._month && _day < d._day) { return true; } else { return false; } } // "==" 运算符重载 bool Date::operator==(const Date& d) { return _year == d._year && _month == d._month && _day == d._day; } // "<=" 运算符重载 bool Date::operator<=(const Date& d) { // 运算符重载的复用 return *this < d || *this == d; } // ">" 运算符重载 bool Date::operator>(const Date& d) { return !(*this <= d); } // ">="运算符重载、 bool Date::operator>=(const Date& d) { return !(*this < d); } //"!="运算符重载 bool Date::operator!=(const Date& d) { return !(*this == d); } // 日期+= 天数: d1 = d1 + 100 Date& Date::operator+=(int day) { // 防止传入的天数为 负数(-100) if (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; } // 日期 + 天数 d1+100 d1不变 Date Date::operator+(int day) { // 调用拷贝构造 Date temp(*this); temp += day; return temp; } // 日期-= 天数 :d1 d1 - 100 Date& Date::operator-=(int day) { if (day < 0) { return *this += (-day); } _day -= day; while (_day <= 0) { _month--; if (_month == 0) { _month = 12; _year--; } _day += GetMonthDay(_year, _month); } return *this; } // 日期-天数 : d1 - 100 Date Date::operator-(int day) { // 调用拷贝构造 Date temp(*this); // 复用 "-=" temp -= day; return temp; } // 前置++ (无参的为前置,有参的为后置) Date& Date::operator++() { // 直接复用 += *this += 1; return *this; } // 后置++(无参的为前置,有参的为后置) // int i 这里的形参可以写,可以不写 Date Date::operator++(int i) { Date temp(*this); *this += 1; return temp; } // 前置-- Date& Date::operator--() { *this -= 1; return *this; } //后置-- Date Date::operator--(int i) { Date temp(*this); *this -= 1; return temp; } // 日期 - 日期 int Date::operator-(const Date& d) { // 方便后续计算正负 int flag = 1; Date max = *this; Date min = d; // 确保max是大的 min是小的 if (*this < d) { min = *this; max = d; flag = -1; //计算正负 } int n = 0; // 计算min和max之间的绝对值差距 while (min != max) { min++; n++; } return n * flag; }
🥝Test.cpp
#define _CRT_SECURE_NO_WARNINGS 1 #include "Date.h" // 测试初始化 void Test() { cout << "请输入今日的日期" << endl; Date d1(2023,10,28); d1.Printf(); } // 测试"<"运算符 void Test1() { Date d1(2023,10,28); cout << "d1的日期为:"; d1.Printf(); cout << "d2的日期为:"; Date d2(2023, 10, 27); d2.Printf(); cout << endl; if (d2 < d1) { cout << "d1的日期 < d2的日期" << endl; } else { cout << "d2的日期 > d1的日期" << endl; } } // d1 + 100 测试 void Test2() { Date d1(2023,10,27); cout << "d1的日期为:"; d1.Printf(); Date d2; d2 = d1 + 100; cout << "d1的日期+100为:"; d2.Printf(); } // d1+=100 测试 void Test3() { Date d1(2023, 10, 28); cout << "d1的日期为:"; d1.Printf(); cout << endl; d1 += 100; cout << "d1的日期+100为:"; d1.Printf(); } // 日期 - 日期 void Test4() { Date d1(2023, 11, 11); cout << "d1的日期为:"; d1.Printf(); cout << "d2的日期为:"; Date d2(2023, 10, 28); d2.Printf(); cout << endl; cout << "d1与d2之间的天数差距为:"; int ret = d1 - d2; cout << ret << endl; } void Test5() { cout << " *********** 欢迎来到 日期计算器 ***********" << endl<<endl; cout << "输入今日的日期: >" ; int y, m, d; cin >> y >> m >> d; cout << endl; Date d1(y, m, d); //d1.Printf(); cout << "输入需要对比的日期: >"; int y1, m1, dd; cin >> y1 >> m1 >> dd; cout << endl; Date d2(y1, m1, dd); //d2.Printf(); cout << "两个日期之间的天数差距为:"; int ret = d - dd; cout << ret << endl; } int main() { Test5(); return 0; }
🍍代码运行的界面
五、共勉
以下就是我对C++日期计算器的理解,如果有不懂和发现问题的小伙伴,请在评论区说出来哦,同时我还会继续更新对C++ 类和对象的理解,请持续关注我哦!!!