个人主页:@Weraphael
✍作者简介:目前学习C++和算法
✈️专栏:课设
希望大家多多支持,咱一起进步!
如果文章对你有帮助的话
欢迎 评论 点赞 收藏 加关注✨
- 知识回顾
这篇博客将运用以上知识来实现一个完整的日期类
为了方便管理,我们可以创建多个文件来实现
#include
using namespace std;
class Date
{
public:
// 友元函数
//friend void operator>>(istream& in, Date& x); 错误(返回类型)
friend istream& operator>>(istream& in, Date& x);
friend ostream& operator<<(ostream& out, const Date& x);
// 写构造函数
// 全缺省(注意:声明给缺省值)
Date(int year = 1, int month = 2, int day = 3);
// 打印成员函数
void Print() const
{
cout << this->Year << '-' << this->Month << '-' << this->Day << endl;
}
// 以下是运算符重载
// ==
bool operator==(const Date& x) const;
// <
bool operator<(const Date& x) const;
// >=
bool operator>=(const Date& x) const;
// <=
bool operator<=(const Date& x) const;
// >
bool operator>(const Date& x) const;
// !=
bool operator!=(const Date& x) const;
// 获取某月的天数
int GetMonthDay(int Year, int Month);
// 日期+=天数
Date& operator+=(int day);
// 日期-=天数
Date& operator-=(int day);
// 日期+day(返回的变量销毁了,不能用引用返回)
Date operator+(int day) const;
// 日期-day
Date operator-(int day) const;
// 前置++()
Date& operator++();
// 后置++
Date operator++(int);
// 前置--
Date& operator--();
// 后置--
Date operator--(int);
// 日期-日期 = 天数(int)
int operator-(const Date& x) const;
private:
int Year; // 年
int Month; // 月
int Day; // 日
};
//流提取
istream& operator>>(istream& in, Date& x);
// 流插入
ostream& operator<<(ostream& out, const Date& x);
成员类型都是内置类型,出了作用域,其变量自动销毁。因此可以不写(但编译器会自动生成,不用管)
编辑器的默认拷贝构造函数对内置类型是直接拷贝的
编辑器的默认赋值操作符重载函数对内置类型也是直接拷贝的
构造函数和以上三个成员函数不同,因为如果不自己定义构造函数,内置类型的默认构造函数是随机值
// 构造函数(声明给缺省值)
Date::Date(int year, int month, int day)
{
this->Year = year;
this->Month = month;
this->Day = day;
}
注意:
- 当全缺省参数声明和定义分离的情况下,声明给缺省值
- 类也可以让声明和定义分离。类声明放在
.h
文件中,成员函数定义放在.cpp
文件中,同时需要使用作用域限定符::
void Print() const
{
cout << this->Year << '-' << this->Month << '-' << this->Day << endl;
}
// ==
bool Date::operator==(const Date& x) const
{
return (Year == x.Year)
&& (Month == x.Month)
&& (Day == x.Day);
}
// <
bool Date::operator<(const Date& x) const
{
// 年份大的则大
if (Year < x.Year)
return true;
// 月份大的则大(前提:月份相同)
else if (Year == x.Year && Month < x.Month)
return true;
// 日份大的则大(前提:年月份相同)
else if (Year == x.Year && Month == x.Month && Day < x.Day)
return true;
// 若不满足以上要求,则为假
else
return false;
}
当以上
<
和==
写完后,以下代码都可以复用了(3.6 ~ 3.9)
bool Date::operator>=(const Date& x) const
{
return !(*this < x);
}
bool Date::operator<=(const Date& x) const
{
return (*this < x || *this == x);
}
bool Date::operator>(const Date& x) const
{
return !(*this <= x);
}
bool Date::operator!=(const Date& x) const
{
return !(*this == x);
}
// 获取某月的天数
int Date::GetMonthDay(int Year, int Month)
{
// 因为GetMonthDay函数会掉用很多次,每次调用都会为GetDay开辟空间
// 因此static修饰的变量就不会再每次调用函数的时候开辟变量空间,减少损耗
static int GetDay[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30,31,30,31 };
// 可能存在在闰年的情况
if (Month == 2 && Year % 400 == 0 || Year % 4 == 0 && Year % 100 != 0)
{
return 29;
}
return GetDay[Month];
}
// 日期+=天数
//d1 += 2
Date& Date::operator+=(int day)
{
if (day < 0)
{
return *this -= -day;
}
this->Day += day;
// 可能会超过月份天数的最大值
while (this->Day > GetMonthDay(this->Year, this->Month))
{
// 超过就减当月的最大值
this->Day -= GetMonthDay(this->Year, this->Month);
// 然后月份进位
this->Month++;
// 如果月份进位超过12
if (this->Month > 12)
{
// 月份轮回
this->Month -= 12;
// 年份进位
this->Year++;
}
}
return *this;
}
日期+=天数
返回的结果是给到对象本身。当生命周期结束后,*this(对象)
不会被销毁,因此可以使用引用返回
Date& Date::operator-=(int day)
{
if (day < 0)
{
return *this += -day;
}
this->Day -= day;
// 减去的日期可能存在负数日期
while (this->Day <= 0)
{
// 月份减一(借位)
this->Month--;
// 可能存在月份借完的情况
if (this->Month <= 0)
{
this->Month += 12;
// 年份借位
this->Year--;
}
this->Day += GetMonthDay(this->Year, this->Month);
}
return *this;
}
// 日期+day
Date Date::operator+(int day) const
{
// 下面是拷贝构造函数,不是赋值运算符重载
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 > 12)
{
tmp.Month -= 12;
tmp.Year++;
}
}
return tmp;*/
// 可以直接复用(最好)
return tmp += day;
}
日期+天数只是计算出一个结果,并没有改变其对象。所以可以用一个临时变量
tmp
来存储结果。但千万不能使用引用返回,因为当生命周期结束,变量tmp
销毁,可能会产生随机值。
Date Date::operator-(int day) const
{
Date tmp = *this;
return tmp -= day;
}
Date& Date::operator++()
{
*this += 1;
return *this;
}
// 后置++
Date Date::operator++(int)
{
Date tmp = *this;
*this += 1;
return tmp;
}
前置++·
和后置++
会发生一个问题:函数名会相同。因此,C++
规定:后置(++/--)
重载时多增加一个int
类型的参数,但调用函数时该参数不用传递。
Date& Date::operator--()
{
*this -= 1;
return *this;
}
Date Date::operator--(int)
{
Date tmp = *this;
*this -= 1;
return tmp;
}
// 思路:循环计数的方法
int Date::operator-(const Date& x) const
{
// 假设一开始日期1比日期大
Date Max = *this;
Date Min = x;
int flag = 1;
// 特判存在日期2比日期1大
if (Min > Max)
{
Max = x;
Min = *this;
flag = -1;
}
int cnt = 0;
while (Min != Max)
{
Min++;
cnt++;
}
return cnt * flag;
}
有的人说了,既然可以打印日期,那能否输入日期呢?其实是可以的。那么能否用
cin
或者printf
函数呢?答案当然不可以。因为printf
只能根据格式化输出来打印的,例如%d
、%f
等内置类型,cout
它是能自动识别类型的(其实可以自动识别类型是因为在库中实现了【如图1】),同样的只能识别内置类型。
因此,为了能让自定函数也能使用到cin
,cin
就可以通过重载>>
运算符来实现输出的操作:
>>
有是一个双操作数:一个是日期类对象,另一个是cin
。注意:cin
是类对象,是istream
的类对象。这个istream
是这个库定义的
istream& operator>>(istream& in, Date& x)
{
int year, month, day;
in >> year >> month >> day;
// 可能存在输入非法日期
if (month > 0 && month < 13 && day > 0 && day <= x.GetMonthDay(year, month))
{
x.Year = year;
x.Month = month;
x.Day = day;
}
else
{
cout << "非法日期" << endl;
}
return in;
}
>>
运算符重载不能成为成员函数的原因:
成员函数的第一个参数默认是this
指针,它是指向当前对象的指针,而这里的第一个参数是cin
,因此不能变成成员函数,那就只能定义在全局了。但是这里有个问题,那就是全局的函数就不能访问类中私有的成员变量了,这里就用 友元函数 来解决此问题,即在函数声明前加上friend
放在类中(详情见Date.h
)- 返回值是
istream&
而不是void
的原因:
如果返回值是void
,那它就不能连续输入。(cin >> d1 >> d2
)因为流插入的顺序是从左向右的,cin >> d1
后,返回的是void
类型,再cin>>d2
就会产生类型冲突,因此我们可以返回d1
的类型来解决此问题,而cin
的类型就是istream
- 两个形参能否用
const
修饰:
in>>d1
,in
是每次都键盘上输入不能的内容,因此不能用const
修饰;而d1
再没获取数据之前可能是随机值,然后再通过键盘获取数据,也就改变了数据内容,因此不能用const
修饰。
<<
有是一个双操作数:一个是日期类对象,另一个是cout
。注意:cout
是类对象,是ostream
的类对象。这个ostream
是这个库定义的
ostream& operator<<(ostream& out, const Date& x)
{
out << x.Year << "年" << x.Month << "月" << x.Day << "日" << endl;
return out;
}
out
不能用const
修饰的原因:
out
相当于终端控制台上,而每次数据输出在控制台都不一样,因此out
不能用const
修饰- 更多详细细节参考流提取
>>
【Date.h】
class Date
{
public:
// 友元函数
friend istream& operator>>(istream& in, Date& x);
friend ostream& operator<<(ostream& out, const Date& x);
// 写构造函数,因为内置类型的默认构造函数是随机值
// 全缺省(注意:声明给缺省值)
Date(int year = 1, int month = 2, int day = 3);
// 不用写析构函数 - 没有向堆申请空间,出了作用域,变量自动销毁
// 不用写拷贝构造函数 - 默认的拷贝构造函数对内置类型是直接拷贝的
// 不用写赋值操作符重载函 - 其默认函数对内置类型也是直接拷贝的
// 打印成员函数
void Print() const
{
cout << this->Year << '-' << this->Month << '-' << this->Day << endl;
}
// ==
bool operator==(const Date& x) const;
// <
bool operator<(const Date& x) const;
// >=
bool operator>=(const Date& x) const;
// <=
bool operator<=(const Date& x) const;
// >
bool operator>(const Date& x) const;
// !=
bool operator!=(const Date& x) const;
// 获取某月的天数
int GetMonthDay(int Year, int Month);
// 日期+=天数
Date& operator+=(int day);
// 日期-=天数
Date& operator-=(int day);
// 日期+day(返回的变量销毁了,不能用引用返回)
Date operator+(int day) const;
// 日期-day(返回的变量销毁了,不能用引用返回)
Date operator-(int day) const;
// 前置++()
Date& operator++();
// 后置++
Date operator++(int);
// 前置--
Date& operator--();
// 后置--
Date operator--(int);
// 日期-日期 = 天数(int)
int operator-(const Date& x) const;
private:
int Year;
int Month;
int Day;
};
//流提取
istream& operator>>(istream& in, Date& x);
// 流插入
ostream& operator<<(ostream& out, const Date& x);
【Date.cpp】
#include "Date.h"
// 构造函数(声明给缺省值)
Date::Date(int year, int month, int day)
{
this->Year = year;
this->Month = month;
this->Day = day;
}
// ==
bool Date::operator==(const Date& x) const
{
return (Year == x.Year)
&& (Month == x.Month)
&& (Day == x.Day);
}
// <
bool Date::operator<(const Date& x) const
{
if (Year < x.Year) return true;
else if (Year == x.Year && Month < x.Month) return true;
else if (Year == x.Year && Month == x.Month && Day < x.Day) return true;
else return false;
}
// 写出了 < 和 == 以下都可以复用
// >=
bool Date::operator>=(const Date& x) const
{
return !(*this < x);
}
// <=
bool Date::operator<=(const Date& x) const
{
return (*this < x || *this == x);
}
// >
bool Date::operator>(const Date& x) const
{
return !(*this <= x);
}
// !=
bool Date::operator!=(const Date& x) const
{
return !(*this == x);
}
// 获取某月的天数
int Date::GetMonthDay(int Year, int Month)
{
static int GetDay[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30,31,30,31 };
if (Month == 2 && Year % 400 == 0 || Year % 4 == 0 && Year % 100 != 0)
{
return 29;
}
return GetDay[Month];
}
// 日期+=天数
Date& Date::operator+=(int day)
{
if (day < 0)
{
return *this -= -day;
}
this->Day += day;
while (this->Day > GetMonthDay(this->Year, this->Month))
{
this->Day -= GetMonthDay(this->Year, this->Month);
this->Month++;
if (this->Month > 12)
{
this->Month -= 12;
this->Year++;
}
}
return *this;
}
// 日期-=天数
Date& Date::operator-=(int day)
{
if (day < 0)
{
return *this += -day;
}
this->Day -= day;
while (this->Day <= 0)
{
// 月份减一(借位)
this->Month--;
// 可能存在月份借完的情况
if (this->Month <= 0)
{
this->Month += 12;
this->Year--;
}
this->Day += GetMonthDay(this->Year, this->Month);
}
return *this;
}
// 日期+day
Date Date::operator+(int day) const
{
// 下面是拷贝构造函数,不是赋值运算符重载
Date tmp = *this;
return tmp += day;
}
// 日期-day
Date Date::operator-(int day) const
{
Date tmp = *this;
return tmp -= day;
}
// 前置++()
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;
}
// 日期1-日期2 = 天数(int)
int Date::operator-(const Date& x) const
{
Date Max = *this;
Date Min = x;
int flag = 1;
if (Min > Max)
{
Max = x;
Min = *this;
flag = -1;
}
int cnt = 0;
while (Min != Max)
{
Min++;
cnt++;
}
return cnt * flag;
}
// 流提取
istream& operator>>(istream& in, Date& x)
{
int year, month, day;
in >> year >> month >> day;
if (month > 0 && month < 13 && day > 0 && day <= x.GetMonthDay(year, month))
{
x.Year = year;
x.Month = month;
x.Day = day;
}
else
{
cout << "非法日期" << endl;
}
return in;
}
//流插入
ostream& operator<<(ostream& out, const Date& x)
{
out << x.Year << "年" << x.Month << "月" << x.Day << "日" << endl;
return out;
}