目录
1.日期类的预览
学习了类和对象这一章节的知识,我们简单地来创建一个日期类
首先我们来整体预览一下日期类的大致结构(在.h头文件中进行声明)
class Date
{
friend ostream& operator<<(ostream& out, const Date& d);
friend istream& operator>>(istream& in, Date& d);
public:
static int GetMonthDay(int year, int month);
Date(int year = 2025, int month = 3, int day = 17);
bool operator<(const Date& d) const;
bool operator==(const Date& d) const;
bool operator>(const Date& d) const;
bool operator>=(const Date& d) const;
bool operator<=(const Date& d) const;
bool operator!=(const Date& d) const;
Date& operator+=(int day);
Date operator+(int day) const;
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);
private:
int _year;
int _month;
int _day;
};
ostream& operator<<(ostream& out, const Date& d);
istream& operator>>(istream& in, Date& d);
注意到,日期类只包含内置类型,所以
析构函数,拷贝构造函数,赋值重载函数都不用手搓
下面我们开始一项项讲解:
2.日期类的具体实现
2.1计算每月的天数
通过创建一个函数,计算每月的天数
int Date::GetMonthDay(int year, int month)
{
int days[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
if (month == 2)
{
if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0))
return 29;
else return 28;
}
else
{
return days[month];
}
}
(1)将 GetMonthDay 声明为静态函数的好处:类里类外都可以访问
(1)创建一个数组存放每一个月的天数,判断闰年返回即可
(1)先判断是否是2月,在判断是否是闰年,效率有所提高
2.2输入输出
我们希望
这样输入一个日期类对象: cin >> d;
这样输出一个日期类对象: cout << d;
显然需要进行运算符重载:
(1)当我们选择将流插入和流提取运算符重载为成员函数时
由于this指针是隐藏的第一个参数,即默认的左操作数
那么我们最终的实现只可能是:
d << cout;(打印d)
d >> cin; (输入d)
这显然不符合常规使用
(2)所以我们选择将流插入和流提取运算符重载为全局函数
又注意到:
类的成员变量一般都是私有的,
想要访问,可以直接声明为友元函数突破封装
或者写几个成员函数返回成员变量的值
下面实现友元函数:
ostream& operator<<(ostream& out, const Date& d)
{
out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
return out;
}
istream& operator>>(istream& in, Date& d)
{
int year, month, day;
in >> year >> month >> day;
if (month > 0 && month < 13 && day >= 1
&& day <= Date::GetMonthDay(year, month))
{
d._year = year;
d._month = month;
d._day = day;
}
else
{
cout << "非法日期" << endl;
assert(false);
}
return in;
}
(1)ostream、istream对象会在函数体内部进行相应改变,不能用const修饰
(1)流插入运算符重载的时候,用const修饰引用d,防止函数体内部的修改
流提取运算符重载的时候,不用const修饰引用d,因为此时就是要改变d
(1)注意非法日期的输入
(1)为了连续的输入输出,需要返回相应的ostream、istream对象
2.3构造函数
我们选择手搓一个全缺省的构造函数,注意非法日期即可
Date::Date(int year = 2025, int month = 3, int day = 17)
{
if (month > 0 && month < 13 && day >= 1 && day <= GetMonthDay(year, month))
{
_year = year;
_month = month;
_day = day;
}
else
{
cout << "非法日期" << endl;
assert(false);
}
}
2.4日期大小的比较
重点就是:学会函数的复用,
只需写一个判断小于的函数和一个判断等于的函数
就可以实现所有大小比较的复用
bool Date::operator<(const Date& d) const
{
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) const
{
return (_year == d._year)
&& (_month == d._month)
&& (_day == d._day);
}
bool Date::operator>(const Date& d) const
{
return !(*this <= d);
}
bool Date::operator>=(const Date& d) const
{
return !(*this < d);
}
bool Date::operator<=(const Date& d) const
{
return (*this < d) || (*this == d);
}
bool Date::operator!=(const Date& d) const
{
return !(*this == d);
}
(1)判断是否等于时,直接返回表示式!!
(1)因为大小比较时,日期对象不会发生改变,加上const修饰,
使得普通对象和const对象都可以调用该函数
2.5日期+=、+、-=、-天数
这里需要区分: date += day和date + day
+= 时,日期对象会发生改变
+ 时, 日期对象不会改变
但它们返回的都是加上天数后的日期,
有返回值才支持连续赋值
(- 和 -= 同理)
Date& Date::operator+=(int day)
{
if (day < 0)
{
return *this -= (-day);
}
_day += day;
while (_day > GetMonthDay(_year, _month))
{
_day -= GetMonthDay(_year, _month);
++_month;
if (_month == 13)
{
_month = 1;
++_year;
}
}
return *this;
}
Date Date::operator+(int day) const
{
Date temp = *this;
temp += day;
return temp;
}
Date& Date::operator-=(int day)
{
if (day < 0)
{
return *this += (-day);
}
_day -= day;
while (_day < 1)
{
--_month;
if (_month == 0)
{
_month = 12;
--_year;
}
_day += GetMonthDay(_year, _month);
}
return *this;
}
Date Date::operator-(int day) const
{
Date temp = *this;
temp -= day;
return temp;
}
(1)同样,学习函数的复用
(1)重载+, 会创建两个临时对象,所以我们选择在 重载+ 时去复用+=
因为如果选择在 重载+= 时去复用+,
调用任意一个运算符都会进行对象的创建,代码效率就会下降
(1)this指针可以在成员函数内部进行使用
2.6++、--日期、日期++、--
Date& Date::operator++()
{
_day++;
if (_day > GetMonthDay(_year, _month))
{
++_month;
if (_month == 13)
{
_month = 1;
++_year;
}
_day = 1;
}
return *this;
}
Date Date::operator++(int)
{
Date temp = *this;
++(*this);
return temp;
}
Date& Date::operator--()
{
_day--;
if (_day == 0)
{
--_month;
if (_month == 0)
{
_month = 12;
--_year;
}
_day = GetMonthDay(_year, _month);
}
return *this;
}
Date Date::operator--(int)
{
Date temp = *this;
--(*this);
return temp;
}
(1)this指针在函数体中的使用
(2)临时对象的创建,注意返回值类型即可
2.7日期之间的天数
int Date::operator-(const Date& d) const
{
Date min = *this;
Date max = d;
int flag = -1;
if (min > max)
{
min = d;
max = *this;
flag = 1;
}
int cnt = 0;
while (min < max)
{
++cnt;
++min;
}
return cnt * flag;
}
(1)默认大日期、默认小日期,用max、min替换(使代码更简洁)
(2)flag的妙用,天数为正还是负