目录
前言
默认构造函数
常见运算符重载
==运算符重载
!=运算符重载
>运算符重载
>=运算符重载
<运算符重载
<=运算符重载
+=运算符重载
+运算符重载
-=运算符重载
-运算符重载(日期减若干天)
前置++运算符重载
后置++运算符重载
-运算符重载(两个日期相减)
流插入&流提取
后记
在初步对类和对象的学习之后,编写一个日期类对小伙伴们来说应该不是大问题,也可以当作是对类和对象的细节知识点的复习,巩固记忆,当然,本文重点是编写日期类中常见运算符重载,其次是如何完整编写一个类,那么,日期类的一些该有的功能具体有哪些呢?包括但不限于下方功能,快来跟我实现它们吧!
参考笔者之前的文章http://t.csdn.cn/ZgU0z,再根据日期类的成员变量的特点,建议自己写构造函数,析构函数、拷贝构造、赋值运算符重载、取地址操作符重载使用系统自带的即可。
CheckDate()是检查日期是否合法,为方便调用直接封装成了一个函数;
Date()是构造函数;
Print()打印日期,实现功能时方便观察;
GetMonthDay()获取某年某月的天数,后面多次会用到,所以等装成了一个函数;
其他成员函数即为下方即将要实现的运算符重载。
代码:
#include
#include
using namespace std;
//时间类
class Date
{
public:
bool CheckDate()
{
if ((_year > 0) && (_month > 0 && _month <= 12) && (_day >= 0 && _day <= GetMonthDay(_year, _month)))
{
return true;
}
return false;
}
Date(int year, int month, int day)
{
this->_year = year;
this->_month = month;
this->_day = day;
assert(CheckDate());
}
void Print()
{
cout << this->_year << "-" << this->_month << "-" << this->_day << endl;
}
//获取某年某月的天数(频繁调用)
int GetMonthDay(int year, int month) //放在类里默认内联
{
static int month_days[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 }; //static保证不用重复开空间
int days = month_days[month];
if ((month==2)&&((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
{
days++;
}
return days;
}
Date& 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);
bool operator<=(const Date& d);
Date& operator+=(int x_day);
Date operator+(int x_day);
Date& operator-=(int x_day);
Date operator-(int x_day);
Date& operator++();//前置
Date operator++(int);//后置
Date& operator--();//前置
Date operator--(int);//后置
int operator-(const Date& d);
private:
int _year;
int _month;
int _day;
};
为了增加代码的可读性,c++引入了运算符重载,与函数重载类似,也有返回值,函数名及参数列表,语法为 返回值类型 operator操作符(参数列表)
注意:
①重载后的操作符含义不能改变;
②操作符重载作为成员函数时,有一个隐藏参数——this指针;
③不能重载一些奇怪的符号,比如:¥......
对于内置类型,直接使用==判断是否相等即可,但对于自定义类型,需要自己写出判断规则,即年、月、日同时相等。
代码:
bool Date::operator==(const Date& d)
{
return (this->_year == d._year) && (this->_month == d._month) && (this->_day == d._day);
}
测试:
对于内置类型,直接使用!=判断是否不相等即可,但对于自定义类型,需要自己写出判断规则,即年、月、日只要有一个不相等就是不相等。
但是,我们已经写过了==的运算符重载,所以这里使用复用的方法,直接使用==判断,然后取反。
代码:
//方法一:
//bool Date::operator!=(const Date& d)
//{
// return (this->_year != d._year) || (this->_month != d._month) || (this->_day != d._day);
//}
//方法二:
bool Date::operator!=(const Date& d)
{
return !(*this == d);
}
测试:
根据日期类的特点,比较两个对象的大小,即先比较年份大小,相等情况下比较月份大小,月份相等情况下,比较日份的大小。
代码:
bool Date::operator>(const Date& d)
{
if (this->_year > d._year)
return true;
else if (this->_year == d._year)
{
if (this->_month > d._month)
return true;
else if (this->_month == d._month)
{
if (this->_day > d._day)
return true;
else
return false;
}
else
return false;
}
else
return false;
}
测试:
对于>=运算符的重载,可以在>运算符的基础之上加上==的判断条件即可,但是考虑一下复用的方法,既然已经写了>和==的重载,>=的重载是不是很容易就搞定了。
代码:
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);
}
前面的运算符的操作数是两个对象,这里的+=是一个对象,另外一个是需要加上的天数,按照+=的原生逻辑,相加之后的值赋给对象,所以这里返回引用。
思路:先将天数加到日份上,判断超过此时月份的天数,就向月份上进一,进一之后判断月份是否超过12,超过就向年份上进一,类似于两个数相加,个位数超过十了就像十位数上进一,十位数超过十了就向百位数上进一。
注意:使用循环判断每一次操作之后的年月日是否合法,因为一开始加上的天数可能涉及好几个月或者好几年。
代码:
Date& Date::operator+=(int x_day)
{
if (x_day < 0) //判断若传入的天数是负数,表示的含义就是减去那么些天,就调用-=运算符
{
return *this -= -x_day;
}
this->_day += x_day;
while (this->_day > this->GetMonthDay(this->_year, this->_month))
{
this->_day -= this->GetMonthDay(this->_year, this->_month);
this->_month++;
if (this->_month > 12)
{
this->_month = 1;
this->_year++;
}
}
return *this;
}
测试:
与+=运算符类似,但相加的结果不赋给对象,这里返回Date类,而不是引用,就是返回一个拷贝,不改变原对象,为了可以使用复用的方法使用到+=,先将元对象拷贝给一个临时对象,将临时对象加上天数,再返回这个临时对象。
代码:
Date Date::operator+(int x_day)
{
if (x_day < 0)
{
return *this - (-x_day);
}
Date tmp(*this);
tmp += x_day;
return tmp;
}
-=运算符与+=运算符重载的实现类似,只不过将日份减去需要减的天数之后,如果小于0了就向月份借一,此时判断月份有无小于0,小于0就向年份借一。
代码:
Date& Date::operator-=(int x_day)
{
if (x_day < 0)
{
return *this += -x_day;
}
_day -= x_day;
while (_day <= 0)
{
_month--;
while (_month == 0)
{
_year--;
_month = 12;
}
_day += GetMonthDay(_year, _month);
}
return *this;
}
测试:
-运算符重载的实现参考+运算符重载的实现。
代码:
Date Date::operator-(int x_day)
{
if (x_day < 0)
{
return *this + x_day;
}
Date tmp(*this);
tmp -= x_day;
return tmp;
}
前置++特点是先++后使用,所以这里先复用+=运算符,将日份加上一,然后直接引用返回。
代码:
Date& Date::operator++()
{
(*this) += 1;
return *this;
}
测试:
后置++特点是先使用后++,所以这里将原对象拷贝给临时对象,将原对象的日份加上一后,返回临时对象。
注意:这里的参数列表多了个int类型,是为了形成重载形式,区别于前置++。
前置--与后置--运算符的重载实现与++类似,这里不再赘述。
代码:
Date Date::operator++(int)
{
Date tmp(*this);
(*this) += 1;
return tmp;
}
我们知道,两个日期相加是没有现实意义的,所以不会去专门实现两个日期相加,但两个日期相减是有现实意义的,表示相差的天数,返回int类型,-运算符需要两个操作数,除了一个*this,还需再传入一个对象,此对象不需发生变化,所以加const修饰,传引用是为了提高效率。
思路:找到两个日期中较大的日期和较小的日期,让小日期++去接近大日期,++的次数就是相差的天数。
代码:
int Date::operator-(const Date& d)
{
int days = 0;
Date bigDate = *this;
Date smallDate = d;
int flag = 0; //标志,判断原本是大日期减小日期还是小日期减大日期,决定着天数是正数还是负数
if (*this < d)
{
bigDate = d;
smallDate = *this;
flag = 1;
}
while (smallDate < bigDate)
{
bigDate -= 1;
days++;
}
return flag == 0 ? days : (-days);
}
测试:
在c++中,输入输出使用cin、cout,对于内置类型,iostream库中有对应函数可以适配实现内置类型的输入输出,但对于自定义类型,除了可以写成员函数print()打印,想用cin、cout就得编写对应重载函数。
查阅文档发现,cin是istream类的对象,cout是ostream的对象,思考一下是重载成全局函数还是类成员函数?
如果重载成成员函数,则是void Date::operator<<(istream& in){ ... },当我们调用时就是Date d; d<
但是,重载成全局函数的话,我们访问不了私有的成员变量,所以这里我们使用友元,将全局函数设置成Date类的友元函数,即在类中加上
friend ostream& operator<<(ostream& out, const Date& d);
friend istream& operator>>(istream& in, Date& d);
代码:
//流插入
inline ostream& operator<<(ostream& out, const Date& d) //注意:这里要加上const,否则像cout<<(d+100);中的d+100是常变量就会报错
{
out << d._year <<"年" << d._month <<"月" << d._day <<"日" << endl;
return out;
}
//流提取
inline istream& operator>>(istream& in, Date& d)
{
in >> d._year >> d._month >> d._day;
assert(d.CheckDate());
return in;
}
测试:
在学习过c++的入门知识点以及类和对象的知识点之后,自主去实现日期类是对这些知识点的一个运用,将零零散散的知识点聚集在一起,给自己查缺补漏,非常的使用,有时间的小伙伴可以去尝试实现一遍,又不太清楚的可以在评论区讨论或者私我,拜拜!