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的妙用,天数为正还是负