文章专栏:c++学习之路
持续更新~
目录
一、日期类介绍
日期函数是用于处理日期数据的函数,通常包括获取当前日期、计算两个日期差值、日期时间比较、日期时间计算等功能。
日期类就是对以上功能的各种实现!!
通过对日期类的学习,我们可以对类和对象有更进一步的认识,同时灵活掌握运算符重载,实现自定义类型的各种运算,但是这些运算必须具备现实意义。
比如,计算两个自定义类型Date 之间的差值,实际意义:两个日期相差多少天
Date d1(2023,2,20); //日期类实例化对象 Date d2(2024,5,10); d2 - d1; //计算两个自定义类型Date 之间的差值,实际意义:两个日期相差多少天
完成日期类的实现,必须先对成员函数有一定的概念和理解!!
二、日期类目标功能
1.日期结构设计
自定义类型名称:Date
类的属性:
int _year;
int _month;
int _day;必要的构造函数,析构函数,拷贝构造函数
2.实现功能的前提函数
运算符重载函数
- < 、>、 ==、 <= 、>= 、!=等关系操作符的重载
- 前置++、后置++、后置-- 、前置--
- 加+、加等+=、减-、减等-=
通过以上重载,可以实现日期的加减运算,增加或减少年、月、日来实现新的日期对象,日期的大小比较等等。
工具函数:
获取每个月的天数
int Date::GetMonthDay(int year, int month) const;
3.必须具备实际意义
Date类需要实现对日期有效性的严格检查,确保月份正常,保证闰年的判断,符合各个月份的实际天数。
比如:
- 月份要在【1,12】区间之内;
- 天数根据年份、月份确定。
三、日期类具体实现
🌷头文件->Date.h
🔥逐步解析
1.成员变量
私有成员
//年月日
int _year;
int _month;
int _day;
2.构造函数
全缺省构造函数,默认日期为1年1月1日。
!!注意:缺省值,不能在头文件和接口实现文件中同时存在。
拷贝构造函数,复制给定日期对象的所有信息。
Date(int year = 1, int month = 1, int day = 1);//构造函数 + 全缺省
Date(const Date& d); // 拷贝构造函数
3.赋值运算符重载 (operator=)
用于拷贝另一个Date对象的日期信息到当前对象。
// 赋值运算符重载
// d2 = d3 -> d2.operator=(&d2, d3)
Date& operator=(const Date& d);
4.算术运算符重载:
+= 和 -= 运算符用于日期增加或减少指定天数。
一般都是先实现+= 和-=,然后+和 - 运算符 复用 += 和-=。
+和 - 运算符分别用于返回增加或减少指定天数后的日期对象,以及两个日期之间的天数差。
Date& operator+=(int day);// 日期+=天数
Date operator+(int day);// 日期+天数
Date operator-(int day);// 日期-天数
Date& operator-=(int day);// 日期-=天数
5.自增/自减运算符重载:
前缀和后缀形式的 ++ 与 - -运算符,用于向前或向后移动一天。
可以复用+= 和 -= ,减少代码量!!
Date& operator++();// 前置++
Date operator++(int);// 后置++,有特殊处理 形参
Date operator--(int);// 后置-- ,有特殊处理 形参
Date& operator--();// 前置--
6.关系运算符重载:
<、>、>=、<= 和 == 分别用于比较两个日期的大小关系。
!= 判断两个日期是否不相等。
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);// !=运算符重载
7.必备方法:
Print() 用于输出日期
GetMonthDay() 根据年份和月份获取该月的天数,考虑了闰年的特殊情况。
8.析构函数:
日期类不存在手动开辟空间的行为,无需再手动析构。
9.流运算符的重载 >> 和<<
两个函数都是全局函数。便于打印自定义类型Date。
为了访问类Date中的私有成员,必须把两个函数设为友元。
ostream& operator<<(ostream& out, const Date& d);
istream& operator>>(istream& in, Date& d);
🔥代码展示
#pragma once
#include<iostream>
using namespace std;
//日期类(完结版 + 六个成员函数)
//缺省参数,不能声明和定义同时定义
class Date
{
public:
Date(int year = 1, int month = 1, int day = 1);//构造函数 + 全缺省
~Date(); //析构
Date(const Date& d); // 拷贝构造函数
void Print();
// 获取某年某月的天数
int GetMonthDay(int year, int month);
// 赋值运算符重载
// d2 = d3 -> d2.operator=(&d2, d3)
Date& operator=(const Date& d);
Date& operator+=(int day);// 日期+=天数
Date operator+(int day);// 日期+天数
//实现 + 运算符重载,直接复用+=,
// 为什么不选择 += 复用 + ? 因为在 + 的重载中,发生拷贝的次数 > +=
Date operator-(int day);// 日期-天数
Date& operator-=(int day);// 日期-=天数
Date& operator++();// 前置++
Date operator++(int);// 后置++,有特殊处理 形参
Date operator--(int);// 后置-- ,有特殊处理 形参
Date& operator--();// 前置--
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);// !=运算符重载
// 日期-日期 返回天数
int operator-(const Date& d);
friend ostream& operator<<(ostream& out, const Date& d);
friend istream& operator>>(istream& in, Date& d);
private:
//年月日
int _year;
int _month;
int _day;
};
ostream& operator<<(ostream& out, const Date& d);
istream& operator>>(istream& in, Date& d);
🌷功能实现 --> Date.cpp
🔥逐步解析:
1.构造函数
🍬全缺省构造函数
首先我们需要提供一个全缺省的构造函数,方便对象的实例化。
这里我们提供默认的(1,1,1)作为缺省值.
需要注意的是,此时我们已经在头文件Date.h中,构造函数的声明中确定了缺省值!!
因此,在Date.cpp函数定义中,我们就不能再写缺省值!!
第二个注意事项:
日期的定义必须符合现实意义。
Date类需要实现对日期有效性的严格检查,确保月份正常,保证闰年的判断,符合各个月份的实际天数。
比如:
- 月份要在【1,12】区间之内;
- 天数根据年份、月份确定。
因此需要对传入的日期进行条件检查!
//构造函数 + 全缺省
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);
Print();
cout << "日期非法" << endl;
}
}
🍬拷贝构造函数
拷贝构造函数,让我们更加灵活的创建对象,以及可以传引用返回!提高效率!
Date::Date(const Date& d) {
_year = d._year;
_month = d._month;
_day = d._day;
}
🍬析构函数
因为我们没有开辟空间,不需要考虑复杂问题。
//析构
Date::~Date()
{
}
2.必备方法
便于实现后面的+=、-=
Print() 用于输出日期
void Date::Print()
{
cout << _year << "/" << _month << "/" << _day << endl;
}
GetMonthDay() 根据年份和月份获取该月的天数,考虑了闰年的特殊情况。
// 获取某年某月的天数
int Date::GetMonthDay(int year, int month)
{
int dayArr[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30,31 };
//月份:2月,闰年
if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
{
return 29;
}
return dayArr[month];
}
3.赋值运算符重载
返回值:传引用返回,注意this指针需要解引用。
参数类型:const 防止不小心修改参数内容
使用引用,提高效率,减少拷贝
函数体: 不要对自己本身进行赋值,直接利用地址相同规避。
// 赋值运算符重载
// this = d
Date& Date::operator=(const Date& d)
{
//不对自己赋值
//引用返回,直接对左操作数进行修改,减少拷贝
if (this != &d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
return *this;
}
3.加减相关的运算符重载
+、 -、 +=、 -=、 前置++、 后置++ 、前置--、 后置–
我们只需要实现+=、-=,然后其他运算符就可以复用 += 、-=来实现,极大节省我们的时间。
🍬+=重载
日期+=天数
因为是+=,对日期本身进行了修改,所以传引用返回。
// 日期+=天数
// d+= 1000 ----> d = d+1000,d 的实际内容发生改变
Date& Date::operator+=(int day)
{
// a+=b,a本身变化了,因此最后返回a
if (day < 0)
{
return *this -= day;
}
_day += day;
while(_day > GetMonthDay(_year, _month))//天数已经超过当前月份
{
_day -= GetMonthDay(_year, _month);
_month++;
if (_month > 12)
{
_year++;
_month = 1;
}
}
return *this;
}
🍬+重载
复用+=,可以实现+重载。
d + 1000 ----> a = d+1000,d 的实际内容不改变,而是,其他临时变量接收结果。
所以不能传回引用 , 这里是临时变量,函数结束后会自动释放,传引用会导致错误。
// 日期+天数
// d + 1000 ----> a = d+1000,d 的实际内容不改变,而是,其他变量接收结果
Date Date::operator+(int day)
{
Date temp(*this);
temp += day;
return temp;
}
🍬前置++ 和 后置++
直接复用+=
为了便于区分前置和后置++,
对于前置++,无参数
对于后置++,设个int形参
// 前置++
Date& Date::operator++()
{
return *this += 1;
}
// 后置++
//先用一个临时变量存储this原先的数值,然后this++,返回临时变量
Date Date::operator++(int)
{
Date temp(*this);
*this+=1;
return temp;
}
🍬 -=、-、前置--、后置--
思路和加法类似!!
// 日期-=天数
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;
}
// 日期-天数
Date Date::operator-(int day) {
Date temp(*this);
temp -= day; //复用-=
return temp;
}
// 前置--
Date& Date::operator--()
{
return *this -= 1;
}
// 后置--
Date Date::operator--(int)
{
Date temp(*this);
*this -= 1;
return temp;
}
4.关系运算符
只需要实现== 、> ,就可以通过复用实现其他操作!
==重载
// ==运算符重载
bool Date::operator==(const Date& d)
{
if (_year == d._year && _month == d._month && _day == d._day)
{
return true;
}
return false;
}
>重载
// >运算符重载
bool Date::operator>(const Date& d)
{
if (_year > d._year)
{
return true;
}
else if (_year == d._year && _month > d._month)
{
return true;
}
else if (_year == d._year && _month == d._month && _day > d._day)
{
return true;
}
return false;
}
其他操作符
// >=运算符重载
bool Date::operator >= (const Date& d)
{
if (*this > d || *this == d)
{
return true;
}
return false;
}
// <运算符重载
bool Date::operator < (const Date& d) {
if (*this >= d)
return false;
return true;
}
// <=运算符重载
bool Date::operator <= (const Date& d) {
if (*this > d)
return false;
return true;
}
// !=运算符重载
bool Date::operator != (const Date& d) {
if (*this == d)
return false;
return true;
}
5.计算日期相差天数
思想:
找出较小的日期,采用++,利用计数器计算出天数,直到两个日期相等。
那么计数器,就是相差天数!
// 日期-日期 返回天数
int Date::operator-(const Date& d) {
Date min = *this;
Date max = d;
if (min > max)
{
swap(min, max);
}
int day = 0;
while (min != max)
{
min++;
day++;
}
return day;
}
6.流重载操作符
需要在类Date的声明中设置为友元!才可以访问私有变量。
ostream& operator<<(ostream& out, const Date& d)
{
out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
return out;
}
istream& operator>>(istream& in, Date& d)
{
in >> d._year >> d._month >> d._day;
return in;
}
五、完整代码
Date.h
#pragma once
#include<iostream>
using namespace std;
//日期类(完结版 + 六个成员函数)
//缺省参数,不能声明和定义同时定义
class Date
{
public:
Date(int year = 1, int month = 1, int day = 1);//构造函数 + 全缺省
~Date(); //析构
Date(const Date& d); // 拷贝构造函数
void Print();
// 获取某年某月的天数
int GetMonthDay(int year, int month);
// 赋值运算符重载
// d2 = d3 -> d2.operator=(&d2, d3)
Date& operator=(const Date& d);
Date& operator+=(int day);// 日期+=天数
Date operator+(int day);// 日期+天数
//实现 + 运算符重载,直接复用+=,
// 为什么不选择 += 复用 + ? 因为在 + 的重载中,发生拷贝的次数 > +=
Date operator-(int day);// 日期-天数
Date& operator-=(int day);// 日期-=天数
Date& operator++();// 前置++
Date operator++(int);// 后置++,有特殊处理 形参
Date operator--(int);// 后置-- ,有特殊处理 形参
Date& operator--();// 前置--
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);// !=运算符重载
// 日期-日期 返回天数
int operator-(const Date& d);
friend ostream& operator<<(ostream& out, const Date& d);
friend istream& operator>>(istream& in, Date& d);
private:
//年月日
int _year;
int _month;
int _day;
};
ostream& operator<<(ostream& out, const Date& d);
istream& operator>>(istream& in, Date& d);
Date.cpp
#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);
Print();
cout << "日期非法" << endl;
}
}
//析构
Date::~Date()
{
}
// 拷贝构造函数
Date::Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
void Date::Print()
{
cout << _year << "/" << _month << "/" << _day << endl;
}
// 获取某年某月的天数
int Date::GetMonthDay(int year, int month)
{
int dayArr[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30,31 };
//月份:2月,闰年
if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
{
return 29;
}
return dayArr[month];
}
// 赋值运算符重载
// this = d
Date& Date::operator=(const Date& d)
{
//不对自己赋值
//引用返回,直接对左操作数进行修改,减少拷贝
if (this != &d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
return *this;
}
// 日期+=天数
// d+= 1000 ----> d = d+1000,d 的实际内容发生改变
Date& Date::operator+=(int day)
{
// a+=b,a本身变化了,因此最后返回a
if (day < 0)
{
return *this -= day;
}
_day += day;
while(_day > GetMonthDay(_year, _month))//天数已经超过当前月份
{
_day -= GetMonthDay(_year, _month);
_month++;
if (_month > 12)
{
_year++;
_month = 1;
}
}
return *this;
}
// 日期+天数
// d + 1000 ----> a = d+1000,d 的实际内容不改变,而是,其他变量接收结果
Date Date::operator+(int day)
{
Date temp(*this);
temp += day;
return temp;
}
// 日期-=天数
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;
}
// 日期-天数
Date Date::operator-(int day) {
Date temp(*this);
temp -= day; //复用-=
return temp;
}
// 前置++
Date& Date::operator++()
{
return *this += 1;
}
// 后置++
//先用一个临时变量存储this原先的数值,然后this++,返回临时变量
Date Date::operator++(int)
{
Date temp(*this);
*this+=1;
return temp;
}
// 前置--
Date& Date::operator--()
{
return *this -= 1;
}
// 后置--
Date Date::operator--(int)
{
Date temp(*this);
*this -= 1;
return temp;
}
// >运算符重载
bool Date::operator>(const Date& d)
{
if (_year > d._year)
{
return true;
}
else if (_year == d._year && _month > d._month)
{
return true;
}
else if (_year == d._year && _month == d._month && _day > d._day)
{
return true;
}
return false;
}
// ==运算符重载
bool Date::operator==(const Date& d)
{
if (_year == d._year && _month == d._month && _day == d._day)
{
return true;
}
return false;
}
// >=运算符重载
bool Date::operator >= (const Date& d)
{
if (*this > d || *this == d)
{
return true;
}
return false;
}
// <运算符重载
bool Date::operator < (const Date& d) {
if (*this >= d)
return false;
return true;
}
// <=运算符重载
bool Date::operator <= (const Date& d) {
if (*this > d)
return false;
return true;
}
// !=运算符重载
bool Date::operator != (const Date& d) {
if (*this == d)
return false;
return true;
}
// 日期-日期 返回天数
int Date::operator-(const Date& d) {
Date min = *this;
Date max = d;
if (min > max)
{
swap(min, max);
}
int day = 0;
while (min != max)
{
min++;
day++;
}
return day;
}
ostream& operator<<(ostream& out, const Date& d)
{
out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
return out;
}
istream& operator>>(istream& in, Date& d)
{
in >> d._year >> d._month >> d._day;
return in;
}