1. 基础内容
这部分在上篇文章中有详细介绍,先实现前面的基础部分
1. 成员变量
日期类我们需要有成员变量,为了后面写函数容易区分变量,我们在成员变量前加上 “_” 。
class Dare
{
private:
int _year;
int _month;
int _day;
};
2. 日期类的构造函数
Date(int year =1,int month =1, int day =1)
{
_year=year;
_month=month;
_day=day;
};
由于日期类不涉及内存开辟的问题,所以我们这里不需要写析构函数,使用默认的即可。
3. 日期的运算符重载
先实现 日期之间的比较
bool 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;
}
else
{
return false;
}
}
bool operator==(const Date& d)
{
return _year == d._year && _month == d._month && _day == d._day;
}
我们先实现 小于 和 等于 这两个操作
对于不等于,大于,大于等于,小于等于,我们通过复用上面的函数即可
bool operator<=(const Date& d)
{
return *this < d || *this == d;
}
bool opeerator>(const Date& d)
{
return !( *this <= d );
}
bool operator>=(const Date& d)
{
return !( *this < d );
}
bool operator!=(const Date& d)
{
return !( *this == d );
}
4. 判断当前月的天数
int GetMonthDay(int year, int month)
{
const static int MonthArray[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};
//使用const防止修改,static多次调用更方便
//数组定义13,下标用月数即可,不用month-1
if(month==2 && (year%4 == 0 && year%100 != 0 || year%400 == 0))
//这里最好先检查month是不是等于2,如果最后检查month==2,效率会慢一点
{
return 29;
}
return MonthArray[month];
}
5. 输出
void Print()
{
cout<<_year<<"/"<<_month<<"/"<<_day<<endl;
}
上面就是之前学的基础部分
下面会对日期类的功能进行完善
2. 功能完善
1. 初始化问题
上面我们写的构造函数,可以完成初始化,但是仅仅是做了赋值操作。
如果出现下面的情况
2023 / 2 / 29
2022 / 13 / 30
2000 / 11 / 90
上面的情况都是非法的日期,如果这种数据进入程序,很可能会出现bug,所以要限制一下。
比如出现非法日期提醒一下
Date(int year =1,int month =1, int day =1)
{
_year = year;
_month = month;
_day = day;
if(month < 1 || month > 12 || day < 1 || day > GetMonthDay( year , month))
//这里一定要先检查month,如果先进行GetMonthDay(),可能会造成越界访问
{
cout<<"非法日期:"<<;
Print();
}
}
这样的话,如果我们输入的数据有问题,就会提示我们
2. 赋值重载
赋值运算符的重载----复制拷贝
void operator=(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
和拷贝构造函数不同
void operator=(const Date& b);
Date(const Date& d);
- 拷贝函数如果不加引用,是为了防止无穷递归,防止每一次传入参数的 Date d 都被识别为拷贝。
而赋值重载可以不加,可以理解为传入的是新创建的局部变量。- 赋值和拷贝的区别
赋值:两个已经存在的对象进行拷贝。
拷贝构造:一个已经存在的对象,去初始化另一个对象。
- 赋值和拷贝的区别
- 这里的赋值重载,可以使用 Date b 进行传参,但是每次传入都会进行一次拷贝,效率会慢一点,所以如果内部没有对传入参数进行修改,可以使用传入引用
void operator=(const Date& b);
这样就不需要拷贝,而且因为加入 const 进行修饰,我们也不能在函数内修改传入参数。
3. 如果连续赋值
int main()
{
Date a(2023, 2, 19);
Date b(2022, 3, 30);
Date c(2023, 11, 20);
a.Print();
b.Print();
c.Print();
a = b = c;
a.Print();
b.Print();
c.Print();
return 0;
}
- 自己给自己赋值
int main()
{
Date d1;
d1=d1;
return 0;
}
虽然上面的写法语法上没有问题,但是没有任何意义,编译器还是会根据代码,自动执行,对没意义的事操作,只会浪费时间,所以我们可以改进一下代码。
Date& operator=(const Date& d)
{
if(this != &d)//判断不能自己给自己赋值,相同的赋值没意义,直接返回
{
_year=d._year;
_month=d._month;
_day=d._day;
}
return *this;
}
- 赋值重载的特点
- 对内置类型来说有用,浅拷贝的足够使用
- 自定义类型会自动调用对应的赋值重载函数
3. 运算符重载的使用价值
也是日期类最主要的部分,对运算符重载的使用
1. 日期+=天数
这里使用 += ,会直接修改原来的值,所以函数内直接对原来的值进行操作。
先将天数直接加在原天数上,如果相加后的结果大于当前月的天数,天数减去当前月的天数,月数++,月数为13的话,年数++,月数从1开始,直到天数小于当前月的天数
Date& operator+=(int day)
{
_day += day;
while(_day > GetMonthDay(_year, _month))
{
_day -= GetMonthDay(_year, _month);
_month++;
if (_month == 13)
{
_year++;
_month = 1;
}
}
return *this;
}
2. 天数+日期
Date Date::operator+(int day)
{
Date tmp = *this;
tmp += day;
return tmp;
}
有了 += ,我们直接复用 += ,创建一个 tmp 进行+=,然后返回 tmp ,这样原来的参数不会改变,也可以完成 + 的操作。
3. += 复用 +
上面实现了 + 复用+= ,如果我们用 + 实现 +=,会不会效率更高?
先实现 +
Date Date::operator+(int day)
{
Date tmp=(*this);
tmp._day += day;
while(tmp._day > GetMonthDay(tmp._year, tmp._month))
{
tmp._day -= GetMonthDay(tmp._year, tmp._month);
tmp._month++;
if (tmp._month == 13)
{
tmp._year++;
tmp._month = 1;
}
}
return tmp;
}
和上面逻辑一样,只不过这里是对赋值后的tmp进行操作,能保证原参数不会改变。
Date& Date::operator+=(int day)
{
Date& Date::operator+=(int day)
{
*this = *this + day;
return *this;
}
使用方面没问题,我们对比一下,两种复用的实现方式,有什么区别。
左边 构造 tmp 拷贝一次
加等拷贝一次
右边 构造 tmp 拷贝一次
加等拷贝一次
左边没有拷贝,只涉及运算修改原数据
右边 *this+day 调用上面的的,两次拷贝
*this=*this+day 赋值
- 相比之下,上面 +复用+= 更好,因为实现 += 没有复用其他函数,直接通过运算得到的,效率上更快。
4. 日期 -=
Date& Date::operator-=(int day)
{
_day -= day;
while (_day <= 0)
{
_day += GetMonthDay(_year, _month);
_month--;
if (_month == 0)
{
_year--;
_month = 12;
}
}
return *this;
}
和上面 += 的实现逻辑类型。
5. 日期 - 天数
Date Date::operator-(int day)
{
Date tmp = *this;
tmp -= day;
return tmp;
}
直接复用,很香。
6. +=/-=负数
对于上面实现的 += ,-= ,如果天数是负数,我们的程序就会出现问题
Date Date::operator-(int day)
{
Date tmp = *this;
tmp -= day;
return tmp;
}
所以就要进行优化,如果 += 传入负数,可以理解为 -= 正数,-= 传入负数,可以理解为 += 正数
所以我们只要判断如果传入的是负数,直接复用另一个即可
Date& Date::operator+=(int day)
{
if (day < 0)
{
*this -= (-day);
return *this;
}
_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)
{
if (day < 0)
{
*this += (-day);
return *this;
}
_day -= day;
while (_day <= 0)
{
_day += GetMonthDay(_year, _month);
_month--;
if (_month == 0)
{
_year--;
_month = 12;
}
}
return *this;
}
7. 前置++,后置++
函数名都是++ ,参数也都只有一个参数,可以认为只有返回值不同。
后置 返回++之前的
前置 返回++之后的
Date operator++();
前置和后置++ 函数名一样,为了做区分,我们把上面这种样子作为前置++,后置++需要我们传入int 类型的参数用来区分
//前置:
Date operator++();
++d1->d1.operator++()
//后置:
Date operator++(int);
d1++ -> operator++(0)//只要传入即可,传入什么都可以,只是进行类型判断,不使用接收值
//后置++的传入参数是为了区分构成函数重载
后置++的参数只是为了调用函数的时候有个区分,函数内参数是没有意义的。
上面的运算符重载也构成函数重载
只有自定义类型可以运算符重载
int 的参数只是为了区分前置和后置++函数的区别
Date Date::operator++()
{
*this += 1;
return *this;
}
//前置没有拷贝,前置平时使用好一点
Date Date::operator++(int)
{
Date tmp(*this);
*this + 1;
return tmp;
}
使用的话直接复用上面的即可。
8. 日期差
如果有两个日期,想要计算两个日期之间差了多少天数。
思路:先判断那个天数大,用小的日期追大的日期,每次++,计数的变量++,直到小的追上大的
int Date::operator-(const Date& d)
{
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;
}
9. 代码
Date.h
#pragma once
#include<iostream>
using namespace std;
class Date
{
public:
Date(int year = 1, int month = 1, int day = 1);//初始化
void Print()const;//输出年份
int GetMonthDay(int year, int month);// 当前月有几天
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);//不等于 !=
Date& operator+=(int day);//加等 +=
Date& operator=(const Date& d);//赋值 =
Date operator+(int day);//加 +
Date& operator-=(int day);//减等 -=
Date operator-(int day);//减 -
Date operator++();//前置++ 前置++
Date operator++(int);//后置++ 后置++
Date operator--();//前置-- 前置--
Date operator--(int);//后置-- 后置--
int operator-(const Date& d);//日期减日期 日期差
private:
int _year;
int _month;
int _day;
};
Date.cpp
#include"Date.h"
Date::Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
if (month < 1 || month>12 || day<1 || day>GetMonthDay(year, month))
{
cout << "非法日期" << ":";
}
}
void Date::Print()const
{
cout << _year << "/" << _month << "/"<<_day << endl;
}
int Date::GetMonthDay(int year, int month)
{
const static 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;
}
return MonthArray[month];
}
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;
}
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);
}
Date& Date::operator+=(int day)
{
if (day < 0)
{
*this -= (-day);
return *this;
}
_day += day;
while(_day > GetMonthDay(_year, _month))
{
_day -= GetMonthDay(_year, _month);
_month++;
if (_month == 13)
{
_year++;
_month = 1;
}
}
return *this;
}
Date& Date::operator=(const Date& d)
{
if (this != &d)//判断不能自己给自己赋值,相同的赋值没意义,直接返回
{
_year = d._year;
_month = d._month;
_day = d._day;
}
return *this;
}
Date Date::operator+(int day)
{
Date tmp = *this;
tmp += day;
return tmp;
}
Date& Date::operator-=(int day)
{
if (day < 0)
{
*this += (-day);
return *this;
}
_day -= day;
while (_day <= 0)
{
_day += GetMonthDay(_year, _month);
_month--;
if (_month == 0)
{
_year--;
_month = 12;
}
}
return *this;
}
Date Date::operator-(int day)
{
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 ret = *this;
*this -= 1;
return ret;
}
int Date::operator-(const Date& d)
{
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;
}