前言
在学习完C++类和对象
后,我们需要一个小程序来帮助我们巩固加深知识点,而日期类
就是一个不错的选择,相信大家看完本文日期类
的讲解后,会对类和对象有更深的理解
为了更加契合工程标准,我们还是用三个文件的方式来实现日期类
Date.h
头文件:用于声明类和方法Date.cpp
源文件:用于实现类中声明的方法Test.cpp
源文件:用于测试日期类的功能
下面正文开始!
1. 日期类的定义
先来定义日期类中的默认成员和需要实现的成员函数:
//类里面短小函数,适合做内联函数,是直接在类里面定义的(类里面定义的函数默认认为是内联函数) class Date { public: //友元函数 //流插入 friend ostream& operator<<(ostream& out, const Date& d); //流提取 friend istream& operator>>(istream& in, Date& d); //构造函数 Date(int year = 2023, int month = 4, int day = 12); //打印 void Print(); //获取月份天数 int GetMonthDay(int year, int 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; //日期+=天数 Date& operator+=(int day); //日期+天数 Date operator+(int day) const; //日期-=天数 Date& operator-=(int day); //日期-天数 Date operator-(int day) const; //日期-日期 int operator-(const Date& d) const; //前置++ Date& operator++(); //后置++ //int参数 仅仅是为了占位,跟前置重载区分 Date operator++(int); //前置-- //--d1 -> d1.operator--() Date& operator--(); //后置-- //d1-- -> d1.operator--(1) Date operator--(int); private: int _year; int _month; int _day; };
2. 获取月份天数
获取月份天数只需要将每个月的天数存放在数组中通过下标访问即可
这里需要注意一个细节,那就是将数组的首元素设为0,从第二个元素开始存放月份天数,因为用户并不知道数组是从0下标开始遍历的,这样处理刚好符合生活规范注意:闰年的二月是29天,这里只需判断是否为闰年做出特殊处理即可
闰年判断:四年一闰且百年不润或者四百年一润
//获取月份天数 int Date::GetMonthDay(int year, int month) { assert(month > 0 && month < 13); int monthArray[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 }; if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400) == 0)) { return 29; } else { return monthArray[month]; } }
3. 构造函数
构造函数中需要判断日期是否合法
合法标准:月份在1~ 12之间,天数在0~本月最大天数之间,这里可以复用上面获取月份天数的方法
//构造函数 Date::Date(int year, int month, int day) { if (month > 0 && month < 13 && (day > 0 && day <= GetMonthDay(year, month))) // 判断日期合法 { _year = year; _month = month; _day = day; } else { cout << "日期非法" << endl; } }
4 运算符重载
4.1 判断等于
两个日期的年月日都相等,则两个日期相等
//判断相等 bool Date::operator==(const Date& d) const { return _year == d._year && _month == d._month && _day == d._day; }
4.2 判断小于
在运算中隐含的this指针
默认是左操作数
日期小于的判断标准:
- 年份小于则小于
- 年份相等,月份小于则小于
- 年份月份都相等,天数小于则小于
//判断小于 bool Date::operator<(const Date& d) const { return _year < d._year || (_year == d._year && _month < d._month) || (_year == d._year && _month == d._month && _day < d._day); }
4.3 复用实现
有了等于和小于我们就可以直接复用它们来实现其他的判断运算符重载
//判断小于等于 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 !(*this == d); }
4.4 流插入和流提取
cout
和cin
只能对内置类型进行输入输出,我们对其进行改动,实现可以对自定义类型的输入输出
//流提取 inline ostream& operator<<(ostream& out, const Date& d) { out << d._year << "年" << d._month << "月" << d._day << "日" << endl; return out; } //流插入 inline istream& operator>>(istream& in, Date& d) { in >> d._year >> d._month >> d._day; return in; }
注意:
cout
为ostream
类型,cin
为istream
类型- 要使
cout
、cin
重载后为左操作数,就需要在类外实现,因为在类中实现的函数默认this为左操作数 - 两个重载函数既要在类外实现,又要能访问类中的成员,此时就需要将它们设为友元函数了
- 两个函数的返回值就是
cout
、cin
本身,避免出现类似cout << d1 << d2
这种情况
5. 日期+/-天数
这里我们主要讲解日期+=天数
,后面的都可以用其复用,基本思想也相差不多
日期+=天数
需要考虑到进位的思想:本月天数满后进位到月份上,本年月份满后进位到年份上,这里复用
GetMonthDay
很方便实现
//日期+=天数 Date& Date::operator+=(int 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 { Date tmp(*this); tmp += day; return tmp; } //日期-=天数 Date& Date::operator-=(int day) { if (day < 0) { *this += -day; return *this; } _day -= day; while (_day <= 0) { --_month; if (_month == 0) { --_year; _month = 12; } _day += GetMonthDay(_year, _month); } return *this; } //日期-天数 Date Date::operator-(int day) const { Date tmp(*this); tmp -= day; return tmp; }
6. 日期-日期
日期+日期
毫无意义,但日期-日期
可以知道两日期相隔的天数
日期相减有两种情况:大日期 - 小日期 和 小日期 - 大日期,其结果一个是大于0一个是小于0的,只需用一个flag做个正负标记即可,这里我们用个简单的方法,先找出较小的日期,然后一天天的递增,记录递增的天数,直到与较大的相等即可然后与flag相乘即为最后的结果
//日期-日期 int Date::operator-(const Date& d) const { Date max = *this; Date min = d; int flag = 1; if (*this < d) { max = d; min = *this; flag = -1; } int n = 0; while (min != max) { ++min; ++n; } return n * flag; }
7. 自加自减
由于前置与后置的自加自减函数返回值不同,这里需要通过占位参数
来帮助编译器区分两者
占位参数:
由于前置与后置的运算符重载函数名一致,此时需要给后置函数多加一个参数,让编译器进行区分,这个参数就是占位参数
,为int
类型
7.1 前置
前置的操作是先进行自加自减,再返回,这里可以复用前面的+=
//前置++ Date& Date::operator++() { *this += 1; return *this; } //前置-- Date& Date::operator--() { *this -= 1; return *this; }
7.2 后置
后置的操作是先记录值,再进行自加自减,最后返回前面记录的值,后置需要借助占位参数,此时编译器会自动传参并区分,这里一样可以复用`+=
//后置++ Date Date::operator++(int) { Date tmp(*this); *this += 1; return tmp; } //后置-- Date Date::operator--(int) { Date tmp(*this); *this -= 1; return tmp; }
注意:
对于自定义类型,在进行自加自减操作时,最好使用前置
操作,因为后置
操作会发生拷贝构造
,造成资源浪费
8. 源代码
这里只展示类的声明和方法的实现,测试环节友友们可以自行完成
Date.h
头文件(声明类和方法)
#pragma once #include <iostream> #include<assert.h> using namespace std; //类里面短小函数,适合做内联函数,是直接在类里面定义的(类里面定义的函数默认认为是内联函数) class Date { public: //友元函数 //流插入 friend ostream& operator<<(ostream& out, const Date& d); //流提取 friend istream& operator>>(istream& in, Date& d); //构造函数 Date(int year = 2023, int month = 4, int day = 12); //打印 void Print(); //获取月份天数 int GetMonthDay(int year, int 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; //日期+=天数 Date& operator+=(int day); //日期+天数 Date operator+(int day) const; //日期-=天数 Date& operator-=(int day); //日期-天数 Date operator-(int day) const; //日期-日期 int operator-(const Date& d) const; //前置++ Date& operator++(); //后置++ //int参数 仅仅是为了占位,跟前置重载区分 Date operator++(int); //前置-- //--d1 -> d1.operator--() Date& operator--(); //后置-- //d1-- -> d1.operator--(1) Date operator--(int); private: int _year; int _month; int _day; }; //定义在头文件中,成为内联函数 //流提取 inline ostream& operator<<(ostream& out, const Date& d) { out << d._year << "年" << d._month << "月" << d._day << "日" << endl; return out; } //流插入 inline istream& operator>>(istream& in, Date& d) { in >> d._year >> d._month >> d._day; return in; }
Date.cpp
源文件(实现类中声明的方法)
#include "Date.h" //获取月份天数 int Date::GetMonthDay(int year, int month) { assert(month > 0 && month < 13); int monthArray[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 }; if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400) == 0)) { return 29; } else { return monthArray[month]; } } //构造函数 Date::Date(int year, int month, int day) { if (month > 0 && month < 13 && (day > 0 && day <= GetMonthDay(year, month))) // 判断日期合法 { _year = year; _month = month; _day = day; } else { cout << "日期非法" << endl; } } void Date::Print() { cout << _year << "年" << _month << "月" << _day << "日" << endl; } //判断相等 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 _year < d._year || (_year == d._year && _month < d._month) || (_year == d._year && _month == d._month && _day < d._day); } //判断小于等于 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 !(*this == d); } //日期+=天数 Date& Date::operator+=(int 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 { Date tmp(*this); tmp += day; return tmp; } //日期-=天数 Date& Date::operator-=(int day) { if (day < 0) { *this += -day; return *this; } _day -= day; while (_day <= 0) { --_month; if (_month == 0) { --_year; _month = 12; } _day += GetMonthDay(_year, _month); } return *this; } //日期-天数 Date Date::operator-(int day) const { Date tmp(*this); tmp -= day; return tmp; } //前置++ Date& Date::operator++() { *this += 1; return *this; } //后置++ Date Date::operator++(int) { Date tmp(*this); *this += 1; return tmp; } //前置-- Date& Date::operator--() { *this -= 1; return *this; } //后置-- Date Date::operator--(int) { Date tmp(*this); *this -= 1; return tmp; } //日期-日期 int Date::operator-(const Date& d) const { Date max = *this; Date min = d; int flag = 1; if (*this < d) { max = d; min = *this; flag = -1; } int n = 0; while (min != max) { ++min; ++n; } return n * flag; }
日期类的实现到这里就介绍结束了,实现过程涉及到了前面学的大部分知识,希望大家看完后能对前面的知识有更深的理解
本篇文章对你由帮助的话,期待大佬们的三连,你们的支持是我最大的动力!
文章有写的不足或是错误的地方,欢迎评论或私信指出,我会在第一时间改正