日期类实现
//Date.cpp
#include"Date.h"
Date::Date(int year, int month, int day) {
_year = year;
_month = month;
_day = day;//很简单的赋值
if (CheckInvalid()) {
printf("日期赋值错误");
}
}
bool Date::CheckInvalid() const {
return _year<1 || _month < 1 || _month>12 || _day>GetMonthDay(_year, _month) || _day < 1;
}
Date::Date(const Date& d) {
_year = d._year;
_month = d._month;
_day = d._day;
}
Date::~Date() {
//由于Date类中并未动态开辟任何空间,所以析构函数可以为空
_year = _month = _day = 0;
}
Date& Date::operator=(const Date& d) {
// 检查自赋值
if (this != &d) {
_year = d._year;
_month = d._month;
_day = d._day;
}
return *this; // 返回当前对象的引用,以实现链式赋值
//返回引用则是因为更有效率,不用再在传值的时候使用拷贝构造
}
bool Date::operator<(const Date& d) const{
if (_year < d._year) {
return true;
}
else if (_year == d._year) {
if (_month < d._month) {
return true;
}
else if (_month == d._month) {
if (_day < d._day) {
return true;
}
}
}
return false;
}
bool Date::operator<=(const Date& d) const{
return *this < d || *this == d;
}
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 _year == d._year &&
_month == d._month &&
_day == d._day;
}
bool Date::operator!=(const Date& d) const{
return !(*this == d);
}
Date Date::operator+(int day) const{
Date tmp(*this);//Date tmp=*this也是拷贝构造,因为tmp先前并不存在
tmp += day;
return tmp;//因为之后tmp就会销毁,所以这里我们并未返回引用,不然会导致野引用,-亦然
}
Date& Date::operator+=(int day) {
_day += day;
while (1) {
if (_day > GetMonthDay(_year, _month)) {
_day -= GetMonthDay(_year, _month);
_month++;
if (_month == 13) {
_month = 1;
_year++;
}
}
else
break;
}
return *this;
}
Date Date::operator-(int day) const{
Date tmp = *this;
tmp -= day;
return tmp;
}
Date& Date::operator-=(int day) {
_day -= day;
while (_day <= 0) {
_month--;
if (_month == 0) {
_year--;
_month = 12;
}
_day += GetMonthDay(_year, _month);
}
return *this;
}
//++d1
Date& Date::operator++() {
(*this) += 1;
return *this;
}
//d1++,加入形参int便于分辨
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;
}
//d1-d2,算两个日期之间的差值
int Date::operator- (const Date& d) const{
int flag = 1;
Date max = *this;
Date min = d;
if (*this < d) {
min = *this;
max = d;
flag = -1;
}
int n = 0;
while (min != max) {
min++;//虽然效率低,但是准的
n++;
}
//这里的min!=max也是有讲究的,!=的话效率高一些,如果用<,就会衍生很多判断
return n * flag;//flag就是判断正负
}
//流插入操作符
ostream& operator<<(ostream& out, const Date& d) {
out << d._year << '/' << d._month << '/' << d._day << endl;//其实这里out跟cout是一样的
return out;
}
//流提取操作符
istream& operator>>(istream& in, Date& d) {
while (1)
{
cout << "请依次输入年月日:>";
in >> d._year >> d._month >> d._day;
if (d.CheckInvalid())
{
cout << "亲,输入了无效日期,请重新输入" << endl;//这里还可以判断正误
}
else
{
break;
}
}
return in;
}
为了方便,这里我们直接把日期类的实现整个搬了过来,而这是存储在Date.cpp中的,Date.h则如下所示:
#include<iostream>
#include<assert.h>
using namespace std;
class Date {
private:
int _year;
int _month;
int _day;
public:
bool CheckInvalid() const;
Date(int year = 1, int month = 1, int day = 1);
Date(const Date& d);
~Date();
Date& operator = (const Date& d);
bool operator<(const Date& d) const;//加const的是const成员函数,适用于只读不写的成员函数
bool operator<=(const Date& d) const;//写法就是在最后加一个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) const;
Date& operator+=(int day);
Date operator-(int day) const;
Date& operator-=(int day);
//++d1
Date& operator++();
//d1++,加入形参int便于分辨
Date operator++(int);
Date& operator--();
Date operator--(int);
//d1-d2,算两个日期之间的差值
int operator-(const Date& d) const;
//本质上声明和定义不分离就是内联
int GetMonthDay(int year, int month) const {
assert(month > 0 && month < 13);
static int MonthDays[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };//这里是为了不让每次调用函数时都需要生成MonthDays数组
if (month == 2 && ((year % 4 == 0) && (year % 100 != 0)) || year % 400 == 0) {
return 29;//return 29,不改变数组,不然后续使用麻烦
//month==2放在前面的原因是因为避免不是2月份的月份还要进行是否是闰年的判断
}
return MonthDays[month];
}
friend ostream& operator<<(ostream& out, const Date& d);//我们用友元声明,是为了访问我们的私有对象
friend istream& operator>>(istream& in, Date& d);
void Print() const{
cout << _year << '/' << _month << '/' << _day << endl;
}
};
ostream& operator<<(ostream& out, const Date& d);//在全局定义,是为了调整this指针的位置,防止其在使用时出现的d1<<cout的情况
istream& operator>>(istream& in, Date& d);
而下面我们就将把一些比较难一些的函数,以及一些需要注意的点为大家解释一下:
1.< > ==等大小比较运算符
首先我们可以先实现<和=,这样子,我们就可以直接运用这两个运算符,在其他函数中复用,而不需要写那么多代码
2.-,算两个日期之间的天数之差
由于直接用两个日期减的话,肯定会很难,进位退位一些的实现都很麻烦,所以我们使用一个比较简单的方法,只是在效率上有所欠缺。
首先,我们假设,this指向的对象为大一些的数,而另一个指向的则是小一些的,定义一个flag为1,用来确定正负;如果假设错误则让它们互换,并且flag变为-1;而后面则是定义一个while循环,然后让min++走到max,循环条件为min!=max,并且定义一个n用来储存两者之间相差的天数,最后输出n*flag即可
3.为什么先实现+=和-=,而不是+和-
这个也很好解释,如果我们先实现+=这些,那么我们的+=不会创建一个临时对象,而+会;而如果先实现+,那么不仅+需要创建临时对象,而且+=也需要,这很浪费效率
4.流插入和流提取
我们在使用中会发现,我们无法对自定义类型使用流插入和流提取符号,而这需要我们在类中自己定义
cout是ostream类中的一个对象,所以我们用out来代替cout,后面的cin同理
我们一开始想到的是直接在类中定义声明,也就是像下面这样写:
我们会发现只有这么写,程序才可以正常运行,而如果我们改成正常的写法呢?
仔细分析,我们会发现,由于隐含的this指针是排在第一个参数的位置上的,而out是第二个参数,做右操作数,所以我们只能这么写。那有什么方法可以让其变成正常的样子吗?
既然我们无法在类中定义,那么我们就可以尝试在全局中使用,但是这也有一个问题,那就是我们需要封装,所以如果定义全局函数,那么如何去调用日期类里面的成员变量呢?这里我们就需要用到friend:友元声明,可以访问我们的私有对象,需要在头文件的类里面声明,然后在全局定义,这样子才得以将我们的对象顺序调换;并且,为了使我们可以连续输出和输入,就像连续赋值一样,我们不用void作为我们的返回值,而是ostream&和istream&,才让我们可以链式操作
5.对日期类初始化的检查
虽然我们创造了日期类的构造函数,但是我们却没有在创建的时候检查我们的日期是否合法,而这让我们创建一个一个新函数来检查:bool CheckInvalid();通过将其放在构造函数之中,我们只用在一开始就检查其是否符合规则
以上就是日期类的一些疑难解答,大家也可以自己观察头文件以及定义文件,希望大家能够在文章中学到东西!下一篇文章我们继续加油!