日期类功能实现
日期类实现
一、自定义类型 判断运算符重载
因为都只是判断,并不修改,所以都加 const
(1)bool operator==(const Date& y) const;
bool Date::operator==(const Date& y) const
{
return _year == y._year
&& _month == y._month
&& _day == y._day;
}
(2)bool operator!=(const Date& y) const;
// d1 != d2
// !=运算符重载
bool Date::operator!=(const Date& y) const
{
//复用
return !(*this == y); //★ this不能显式写形参、实参,但可以显式使用
}
★ this不能显式写形参、实参,但可以显式使用
(3)bool operator>(const Date& y) const;
// >运算符重载
bool Date::operator>(const Date& y) const
{
if (_year > y._year)
{
return true;
}
else if (_year == y._year && _month > y._month)
{
return true;
}
else if (_year == y._year && _month == y._month && _day > y._day)
{
return true;
}
return false;
}
实现 > 和 == 剩下的都能通过 复用 来实现
(4)bool operator>=(const Date& y) const;
// >=运算符重载
//实现 > 和 == 剩下的都能通过 复用 来实现
bool Date::operator>=(const Date& y) const
{
return *this > y || *this == y;
}
(5)bool operator<(const Date& y) const;
// <运算符重载
bool Date::operator<(const Date& y) const
{
return !(*this >= y);
}
(6)bool operator<=(const Date& y) const;
// <=运算符重载
bool Date::operator<=(const Date& y) const
{
return *this < y || *this == y;
}
二、日期类中功能的实现
重载哪些运算符,主要看这个运算符有没有意义。有意义就可以实现,没意义不实现
(一)获取某年某月的天数
// 获取某年某月的天数
int Date::GetMonthDay(int year, int month)
{
assert(year >= 1 && month >= 1 && month <= 12); // 容易出现不合法的日期,要加强检查
//直接让下标对应月份,取出相应的天数即可 //没有必要用 switch,case,if,else 什么的
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];
}
(二)多少天以后 是几月几号
(1) 通过 += 来实现 +
- 先用 += 算出对应的多少天以后是几月几号 【+= 改变原日期】
//一、用+=实现+
// d1 += 100 [ 先用 += 算出对应的多少天以后是几月几号 ]
Date& Date::operator+=(int day)
{
// 若day < 0,则复用operator-=
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;
}
- 再用 += 来实现 +
+和-,对原日期并不产生影响【 所以加 const 】,+=/-= 才是对传过来的*this有影响,才是要改变原日期
// +和-,对原日期并不产生影响,+=/-= 才是对传过来的*this有影响,才是要改变原日期
// tmp是局部变量,出了作用域就销毁了,所以不能用Date&传引用返回,只能传值返回
//1.通过+,用tmp返回
Date Date::operator+(int day) const
{
Date tmp(*this); //拷贝构造tmp【 一个已存在的类 拷贝构造给 另一个新创建的类 】
tmp += day;
return tmp;
}
tmp是局部变量,出了作用域就销毁了,所以不能用Date&传引用返回,只能传值返回
(2) 通过 + 来实现 +=
- 再用 + 来实现 +=
Date Date::operator+(int day)const
{
Date tmp(*this); //+对原日期不影响,所以开辟新对象,来进行操作
tmp._day += day;
while (tmp._day > tmp.GetMonthDay(tmp._year, tmp._month))
{
tmp._day -= tmp.GetMonthDay(tmp._year, tmp._month);
++tmp._month;
if (tmp._month == 13)
{
tmp._year++;
tmp._month = 1;
}
}
return tmp;
}
- 用 + 算出对应的多少天以后是几月几号
//二、用 + 实现 += [ 先用 + 算出对应的多少天以后是几月几号 ]
//2.通过 +=,直接对 *this 进行修改,Date& 进行返回
Date& Date::operator+=(int day)
{
//是否还需要创建一个新类专门用来 += //不用,+= 本来就是在对原日期进行修改,所以直接对*this进行处理即可
/*Date d = *this + day;
*this = d;*/
*this = *this + day; //注意:这里不能*this+=day; 这里本来就还在创建 += 这个变量,若用+=则会复用现在的operator+=
//再通过 operator+ 来算出对应的多少天以后是几月几号
return *this;
}
是否还需要创建一个新类专门用来 += ?
不用,+= 本来就是在对原日期进行修改,所以直接对 *this 进行处理即可
对比(1)和(2)
(1)+
两次拷贝 _ 1. += 时的拷贝 2. return值(传值返回) 时的 临时拷贝返回
(2)+ =
直接对原数进行修改,然后传引用返回
-
调用
+
=
赋值也是一种拷贝 【拷贝是一个很耗性能的一个东西(要是拷贝的是一棵10w个节点的二叉树,则是一个巨大的工程,跟重建一棵二叉树 没差)】 -
+ =
可以直接对this作更改,Date & 可传引用返回,不用传值拷贝返回,多调用+ =
便可
(三)多少天前 是几月几号( -= 实现 )
多用 += 和 -= 实现,可以传引用,提高效率
//多用+=和-=实现,可以传引用
Date& Date::operator-=(int day)
{
if (day < 0)
{
return *this += (-day);
}
_day -= day;
while (_day <= 0)
{
--_month;
if (_month == 0)
{
_year--; //月变化,也会影响到年 ,所以先确定好年月,才能GetMonthDay【因为GetMonthDay取决于你是否是闰年,以及月份,才能得到该年该月准确的MonthDay】
_month = 12;
}
_day += GetMonthDay(_year, _month);
}
return *this;
}
(三)多少天前 是几月几号( - 实现 )
// -= 实现 -
Date Date::operator-(int day)const
{
Date tmp(*this);
tmp -= day;
return tmp;
}
(四)相差多少天【 倒计时:还剩多少天 】
// d1 - d2
int Date::operator-(const Date& d) const
{
//假设左小右大
int flag = 1;
Date max = *this;
Date min = d;
//假设错了,左小右大
if (*this < d)
{
max = d;
min = *this;
flag = -1;
}
// 1.直接让n一路++,直到min==max,n++到的就是相差的天数(思路更优,更简单) // 2. 比较天 再比较月,年 =》 判断是否需要借位,借前一个月的天数,还是不够再借
int n = 0;
while (min!=max)
{
++min;
++n;
}
return n * flag;
}
(五)前置++
// 前置++
// ++d1
Date& Date::operator++()
{
*this += 1;
return *this;
}
(六)后置++
// 后置++
// d1++
Date Date::operator++(int)
{
Date tmp(*this);
*this += 1;
return tmp; //tmp返回 => 返回类型 Date
}
(七)前置- -
//前置--
Date& Date::operator--()
{
*this -= 1;
return *this;
}
(八)后置- -
//后置--
Date Date::operator--(int)
{
Date tmp(*this);
*this -= 1;
return tmp; //tmp返回 => 返回类型 Date
}
输入输出 前言
成员函数的一大特点:参数中含有隐藏的 this指针
//隐藏的this指针
void Date::operator<<(ostream& out)
{
out << _year << "年" << _month << "月" << _day << "日" << endl;
}
// 结果,运用<<操作符时,d << out 【 不符合可读性 】
结果,运用<<操作符时,d << out 【 不符合可读性 】
解决方式:
-
放域外,就不是成员函数,就没有隐藏的this指针,不需要操作符左边第一个参数一定得是d,而可以是out
-
但在域外无法访问类里的private私有成员变量 => 将其设置成 友元函数,就可以访问了
(九)输出日期
// 放域外,就不是成员函数,就没有隐藏的this指针,不需要操作符左边第一个参数一定得是d,而可以是out
// 但在域外无法访问类里的private私有成员变量 => 将其设置成 友元函数,就可以访问了
ostream& operator<<(ostream& out, const Date& d) //输出不需要修改 对象d ,so 加const修饰
{
out << d._year << "年" << d._month << "月"<< d._day << "日" << endl;
return out;
}
(十)输入日期
istream& operator>>(istream& in, Date& d) //输出要修改 so 不加const修饰
{
in>> d._year >> d._month >> d._day;
return in;
}
三、完整码源
(一)Date.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<assert.h>
using namespace std;
class Date
{
public:
Date(int year = 1, int month = 1, int day = 1);
/*~Date();*/ //内置类型成员,并没有涉及资源开辟问题,销毁时不需要资源清理,最后系统直接将其内存回收即可; => 没有必要调用析构函数了
void Print();
// 获取某年某月的天数
int GetMonthDay(int year, int month);
//判断 运算符重载
bool operator==(const Date& y) const;
bool operator!=(const Date& y) const;
bool operator>(const Date& y) const;
bool operator<(const Date& y) const;
bool operator>=(const Date& y) const;
bool operator<=(const Date& y) const;
//重载哪些运算符,主要看这个运算符有没有意义
//有意义就可以实现,没意义不实现
//day天后是几月几号
Date& operator+=(int day);
Date operator+(int day)const;
//day天前是几月几号
Date& operator-=(int day);
Date operator-(int day)const;
//倒计时:还剩多少天
int operator-(const Date& d)const;
Date& operator++();
Date operator++(int);
Date& operator--();
Date operator--(int);
//void operator << (ostream& out);
//默认构造函数——取地址 以及 const取地址 操作符重载
Date* operator&()
{
cout << "Date* operator&()" << endl;
return (Date*)0x12233222; //故意让别人知道
}
const Date* operator&()const
{
cout << "const Date* operator&()const" << endl;
//return this;
return nullptr;
}
//友元函数
friend ostream& operator<<(ostream& out, const Date& d); //输出到out,不改变d
friend istream& operator>>(istream& in, Date& d);
private:
int _year;
int _month;
int _day;
};
// 重载操作符必须有一个类类型参数->不能通过重载运算,改变内置类型的运算规则
// int operator+(int i, int j)
(二)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))
{
Print();
cout << "日期非法" << endl;
}
}
void Date::Print() {
cout << _year << "/" << _month << "/" << _day << endl;
}
bool Date::operator==(const Date& y) const
{
return _year == y._year
&& _month == y._month
&& _day == y._day;
}
// d1 != d2
// !=运算符重载
bool Date::operator!=(const Date& y) const
{
//复用
return !(*this == y); //★ this不能显式写形参、实参,但可以显式使用
}
// >运算符重载
bool Date::operator>(const Date& y) const
{
if (_year > y._year)
{
return true;
}
else if (_year == y._year && _month > y._month)
{
return true;
}
else if (_year == y._year && _month == y._month && _day > y._day)
{
return true;
}
return false;
}
// >=运算符重载
//实现 > 和 == 剩下的都能通过 复用 来实现
bool Date::operator>=(const Date& y) const
{
return *this > y || *this == y;
}
// <运算符重载
bool Date::operator<(const Date& y) const
{
return !(*this >= y);
}
// <=运算符重载
bool Date::operator<=(const Date& y) const
{
return *this < y || *this == y;
}
// 获取某年某月的天数
int Date::GetMonthDay(int year, int month)
{
assert(year >= 1 && month >= 1 && month <= 12); // 容易出现不合法的日期,要加强检查
//直接让下标对应月份,取出相应的天数即可 //没有必要用 switch,case,if,else 什么的
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];
}
//一、用+=实现+
// d1 += 100 [ 先用 += 算出对应的多少天以后是几月几号 ]
Date& Date::operator+=(int day)
{
// 若day < 0,则复用operator-=
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有影响,才是要改变原日期
// tmp是局部变量,出了作用域就销毁了,所以不能用Date&传引用返回,只能传值返回
//1.通过+,用tmp返回
Date Date::operator+(int day) const
{
Date tmp(*this); //拷贝构造tmp【 一个已存在的类 拷贝构造给 另一个新创建的类 】
tmp += day;
return tmp;
}
二、用+实现+= [ 先用 + 算出对应的多少天以后是几月几号 ]
//
2.通过+=,直接对*this进行修改,Date&进行返回
//Date& Date::operator+=(int day)
//{
// //是否还需要创建一个新类专门用来+= //不用,+=本来就是在对原日期进行修改,所以直接对*this进行处理即可
// /*Date d = *this + day;
// *this = d;*/
//
// *this = *this + day; //注意:这里不能*this+=day; 这里本来就还在创建 += 这个变量,若用+=则会复用现在的operator+=
// //再通过 operator+ 来算出对应的多少天以后是几月几号
// return *this;
//}
//
//
//Date Date::operator+(int day)const
//{
// Date tmp(*this); //+对原日期不影响,所以开辟新对象,来进行操作
// tmp._day += day;
// while (tmp._day > tmp.GetMonthDay(tmp._year, tmp._month))
// {
// tmp._day -= tmp.GetMonthDay(tmp._year, tmp._month);
//
// ++tmp._month;
// if (tmp._month == 13)
// {
// tmp._year++;
// tmp._month = 1;
// }
// }
//
// return tmp;
//}
//对比 一、和 二、
//(1)`+` 两次拷贝 _ 1. += 时的拷贝 2. return值(传值返回) 时的 临时拷贝返回
//(2)`+ = ` 直接对原数进行修改,然后传引用返回
//调用 `+`
//`=` 赋值也是一种拷贝 【拷贝是一个很耗性能的一个东西(要是拷贝的是一棵10w个节点的二叉树,则是一个巨大的工程,跟重建一棵二叉树 没差)】
//`+ = ` 可以直接对this作更改,Date & 可传引用返回,不用传值拷贝返回,多调用`+ = ` 便可
//多用+=和-=实现,可以传引用
Date& Date::operator-=(int day)
{
if (day < 0)
{
return *this += (-day);
}
_day -= day;
while (_day <= 0)
{
--_month;
if (_month == 0)
{
_year--; //月变化,也会影响到年 ,所以先确定好年月,才能GetMonthDay【因为GetMonthDay取决于你是否是闰年,以及月份,才能得到该年该月准确的MonthDay】
_month = 12;
}
_day += GetMonthDay(_year, _month);
}
return *this;
}
// -= 实现 -
Date Date::operator-(int day)const
{
Date tmp(*this);
tmp -= day;
return tmp;
}
// 前置++
// ++d1
Date& Date::operator++()
{
*this += 1;
return *this;
}
// 后置++
// d1++
Date Date::operator++(int)
{
Date tmp(*this);
*this += 1;
return tmp; //tmp返回 => 返回类型 Date
}
//前置--
Date& Date::operator--()
{
*this -= 1;
return *this;
}
//后置--
Date Date::operator--(int)
{
Date tmp(*this);
*this -= 1;
return tmp; //tmp返回 => 返回类型 Date
}
// d1 - d2
int Date::operator-(const Date& d) const
{
//假设左小右大
int flag = 1;
Date max = *this;
Date min = d;
//假设错了,左小右大
if (*this < d)
{
max = d;
min = *this;
flag = -1;
}
// 1.直接让n一路++,直到min==max,n++到的就是相差的天数(思路更优,更简单) // 2. 比较天 再比较月,年 =》 判断是否需要借位,借前一个月的天数,还是不够再借
int n = 0;
while (min!=max)
{
++min;
++n;
}
return n * flag;
}
//隐藏的this指针
//void Date::operator<<(ostream& out)
//{
// out << _year << "年" << _month << "月" << _day << "日" << endl;
//}
// 结果,运用<<操作符时,d << out 【 不符合可读性 】
// 放域外,就不是成员函数,就没有隐藏的this指针,不需要操作符左边第一个参数一定得是d,而可以是out
// 但在域外无法访问类里的private私有成员变量 => 将其设置成 友元函数,就可以访问了
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::~Date()
//{
//}