个人主页:Jason_from_China-CSDN博客
所属栏目:C++系统性学习_Jason_from_China的博客-CSDN博客
所属栏目:C++知识点的补充_Jason_from_China的博客-CSDN博客
前言
日期类是六个成员函数学习的总结和拓展,是实践的体现
创建文件
构造函数
//.h文件 #pragma once #include<iostream>//流文件 #include<assert.h>//断言头文件 using namespace std;//域展开 class Date { public: //构造函数 Date(int year = 1901, int month = 1, int day = 1); //打印函数 void print(); private: int _year; int _month; int _day; }; //.cpp文件 #include"Date.h" //构造函数 Date::Date(int year, int month, int day) :_year(year) , _month(month) , _day(day) {} //打印函数 void Date::print() { cout << _year << "/" << _month << "/" << _day << endl << endl; } //测试文件 #include"Date.h" int main() { //日期类构造函数的测试 Date d1(111,1,1); d1.print(); return 0; }
这里顺手实现一个打印函数, 方便调试,测试一下没有问题
析构函数
日期类是不需要手动实现析构函数的,因为这里是没有开辟的动态空间,默认生成的析构函数已经完全够用
拷贝构造
//拷贝构造函数 Date::Date(const Date& d) { _year = d._year; _month = d._month; _day = d._day; }
- 实现逻辑:
_year = d._year;
:将传入对象d
的年份赋值给新创建对象的年份成员变量。_month = d._month;
:将传入对象d
的月份赋值给新创建对象的月份成员变量。_day = d._day;
:将传入对象d
的日期赋值给新创建对象的日期成员变量。通过这三个赋值操作,新创建的
Date
对象的成员变量被初始化为与传入对象完全相同的值,从而实现了对象的拷贝
运算符重载比较日期的综合实现
等于的实现(==)
//运算符重载 bool Date::operator==(const Date& d)const { return _year == d._year && _month == d._month && _day == d._day; }
实现逻辑
- 首先比较两个对象的年份
_year
是否相等,如果不相等则直接返回false
,表示两个日期不同。- 如果年份相等,接着比较月份
_month
是否相等。如果不相等,同样返回false
。- 如果年份和月份都相等,最后比较日期
_day
是否相等。如果不相等,返回false
。- 如果年份、月份和日期都相等,那么返回
true
,表示两个日期对象相等。
小于的实现(<)
bool Date::operator<(const Date& d)const { if (this->_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; } return false; }
实现逻辑
- 首先比较两个日期对象的年份。如果当前对象的年份
_year
小于传入对象的年份d._year
,说明当前对象的日期更早,直接返回true
。- 如果年份相等,接着比较月份。如果当前对象的月份
_month
小于传入对象的月份d._month
,说明当前对象的日期仍然更早,返回true
。- 如果年份和月份都相等,最后比较日期。如果当前对象的日期
_day
小于传入对象的日期d._day
,说明当前对象的日期更早,返回true
。- 如果以上三个条件都不满足,即年份、月份和日期都不小于传入对象相应的值,说明当前对象的日期不早于传入对象的日期,返回
false
。
小于等于实现(<=)
bool Date::operator<=(const Date& d)const { return *this < d || *this == d; }
实现逻辑
return *this < d || *this == d;
这行代码使用了逻辑或(||
)运算符来判断当前对象是否小于等于传入的对象。
*this < d
表示调用已经重载的小于运算符<
,判断当前对象是否小于传入的对象。如果这个条件为真,那么当前对象肯定小于等于传入的对象,直接返回true
。*this == d
表示调用已经重载的等于运算符==
,判断当前对象是否等于传入的对象。如果当前对象不小于传入的对象(即*this < d
为假),但两个对象相等,那么当前对象也小于等于传入的对象,同样返回true
。- 如果以上两个条件都不满足,即当前对象既不小于传入的对象也不等于传入的对象,那么返回
false
,表示当前对象大于传入的对象,不满足小于等于的条件。
大于的实现(>)
bool Date::operator>(const Date& d)const { return !(*this <= d); }
实现逻辑
return!(*this <= d);
这行代码利用了逻辑非(!
)运算符和已经实现的小于等于运算符<=
来判断当前对象是否大于传入的对象。
*this <= d
会调用已经重载的小于等于运算符,判断当前对象是否小于等于传入的对象。如果这个条件为真,说明当前对象不大于传入的对象。- 对
*this <= d
取逻辑非,即!(*this <= d)
。如果*this <= d
为假,那么逻辑非的结果为真,这就表示当前对象大于传入的对象,直接返回true
;如果*this <= d
为真,那么逻辑非的结果为假,这就表示当前对象不大于传入的对象,返回false
。
大于等于的实现(>=)
bool Date::operator>=(const Date& d)const { return !(*this < d); }
实现逻辑
return!(*this < d);
这行代码利用了逻辑非(!
)运算符和已经实现的小于运算符<
来判断当前对象是否大于等于传入的对象。
*this < d
会调用已经重载的小于运算符,判断当前对象是否小于传入的对象。如果这个条件为真,说明当前对象小于传入的对象,那么当前对象就不大于等于传入的对象。- 对
*this < d
取逻辑非,即!(*this < d)
。如果*this < d
为假,那么逻辑非的结果为真,这就表示当前对象不小于传入的对象,也就是大于等于传入的对象,直接返回true
;如果*this < d
为真,那么逻辑非的结果为假,这就表示当前对象小于传入的对象,不满足大于等于的条件,返回false
。
代码实现
//.h文件 #pragma once #include<iostream>//流文件 #include<assert.h>//断言头文件 using namespace std;//域展开 class Date { public: //构造函数 Date(int year = 1901, int month = 1, int day = 1); //打印函数 void print(); //运算符重载 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; private: int _year; int _month; int _day; }; //.cpp实现文件 #include"Date.h" //构造函数 Date::Date(int year, int month, int day) :_year(year) , _month(month) , _day(day) {} //打印函数 void Date::print() { cout << _year << "/" << _month << "/" << _day << endl << endl; } //运算符重载 bool Date::operator==(const Date& d)const { return _year == d._year && _month == d._month && _day == d._day; } bool Date::operator<(const Date& d)const { if (this->_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; } 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); } //测试文件 #include"Date.h" int main() { //日期类构造函数的测试 Date d1(111,1,1); d1.print(); //日期类拷贝构造函数的测试 Date d2(d1); d2.print(); //日期类获取每个月天数的测试 int ret1 = d2.GetmonthDay(1999, 2); cout << ret1 << endl << endl; //比较测试(没有问题) Date d3(1, 1, 1); Date d4(1, 1, 1); Date d5(2, 2, 2); bool ret = d3.operator==(d4); bool ret2 = d3.operator<(d5); bool ret3 = d3.operator<=(d4); bool ret4 = d5.operator<=(d3); bool ret5 = d5.operator<(d3); cout << ret << endl << endl; cout << ret2 << endl << endl; cout << ret3 << endl << endl; cout << ret4 << endl << endl; cout << ret5 << endl << endl; return 0; }
测试:没有问题
获取某一年的某一月份的天数(为日期的加减做下铺垫)
我们进行计算的时候是需要找到某一年的某一月份的天数,然后才能进行加减
这里我们直接写成内联函数,在类里面实现,因为每次计算都需要进行计算时间,我们直接重载为成员函数,这样空间就是开辟好的,节约时间,这也是C++比其他语言迅速的原因,不仅仅是实现上面,还有平时书写习惯上面
- 这里一个关键点就是getmonthday没有做定义和声明的分离,因为这里超频繁的调用,所以我们定义为内联,频繁的调用定义为内联提高效率
- 这里返回的是月份的天数
- 这里判断的时候,有一个小细节,就是先判断月份,因为先判断月份比较简单,后判断年份,这样会稍微提高速度
//这里是放到类里面的 //获取一个日期(内联函数) int GetmonthDay(int year, int month)const { assert(month < 13 && month>0); static int monthDayArray[13] = { -1,31,28,31,30,31,30,31,31,30,31,30,31 }; //四年一润,百年不润。四百年一润。 if ((month == 2) && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))) { return 29; } else { return monthDayArray[month]; } }
如果是闰年就单独判断一下,四年一润,百年不润,四百年润一次,并且同时月份是二月(每四年会多一天,365天多五小时+),对逻辑进行修改(因为是不是二月比计算更容易判断)
运算符重载+=,+和-=,-的实现
+=的实现
//计算加法 Date& Date::operator+=(int day) { if (day < 0) { return *this -= day; } _day += day; while (_day > GetmonthDay(_year, _month)) { _day -= GetmonthDay(_year, _month); _month += 1; if (_month > 12) { _year += 1; _month = 1; } } return *this; }
实现逻辑
- 首先判断传入的天数
day
是否小于 0,如果是,则调用减法赋值运算符(-=
)来处理负数的情况,实现了一种统一的处理方式,避免了重复编写类似的代码。- 如果
day
大于等于 0,则将其加到当前日期对象的天数_day
上。- 然后进入一个循环,检查当前日期的天数
_day
是否大于当前年份和月份对应的天数(通过调用GetmonthDay(_year, _month)
获取)。
- 如果是,说明当前日期已经超出了当月的天数范围。此时,将当前日期的天数减去当月的天数,即
_day -= GetmonthDay(_year, _month)
,然后月份_month
加 1。- 如果月份
_month
超过了 12,说明已经到了下一年,此时将年份_year
加 1,并将月份重置为 1。- 循环继续检查新的日期是否仍然超出当月的天数范围,直到日期合法为止。
- 最后,返回当前日期对象的引用,以便支持连续的加法赋值操作。
+的实现(复用+=)
Date Date::operator+(int day)const { Date tmp = *this; tmp += day;//这里需要注意一个点,不能是this的复用,this的复用会导致自身变化 return tmp; }
实现逻辑
Date tmp = *this;
:创建一个临时的Date
对象tmp
,并使用当前对象(*this
)进行初始化。这样就复制了当前对象的状态到tmp
中。tmp += day;
:调用已经重载的加法赋值运算符+=
,在临时对象tmp
上加上指定的天数day
。这个操作会修改tmp
的日期,但不会影响原来的对象(因为tmp
是一个对象)。return tmp;
:返回修改后的临时对象tmp
,作为加上指定天数后的新日期对象。
-=的实现
//日期类的实现-= Date& Date::operator-=(int day) { if (day < 0) { return *this += day; } _day -= day; while (_day <= 0) { _month -= 1; if (_month == 0) { _year -= 1; _month = 12; } _day += GetmonthDay(_year, _month); } return *this; }
实现逻辑
- 首先判断传入的天数
day
是否小于 0,如果是,则调用加法赋值运算符(+=
)来处理负数的情况,实现了一种统一的处理方式,避免了重复编写类似的代码。- 如果
day
大于等于 0,则将其从当前日期对象的天数_day
中减去。- 然后进入一个循环,检查当前日期的天数
_day
是否小于等于 0。
- 如果是,说明当前日期已经超出了合法范围,需要进行调整。首先将月份
_month
减 1。- 如果月份
_month
减到 0,说明已经到了上一年,此时将年份_year
减 1,并将月份重置为 12。- 接着,将减去的天数加上当前调整后的年份和月份对应的天数(通过调用
GetmonthDay(_year, _month)
获取),以确保日期的合法性。- 循环继续检查新的日期是否仍然小于等于 0,直到日期合法为止。
- 最后,返回当前日期对象的引用,以便支持连续的减法赋值操作。
-的实现(复用-=)
Date Date::operator-(int day)const { Date tmp = *this; tmp -= day; return tmp; }
实现逻辑
Date tmp = *this;
:创建一个临时的Date
对象tmp
,并使用当前对象(*this
)进行初始化。这样就复制了当前对象的状态到tmp
中。tmp -= day;
:调用已经重载的减法赋值运算符-=
,在临时对象tmp
上减去指定的天数day
。这个操作会修改tmp
的日期,但不会影响原来的对象(因为tmp
是一个副本)。return tmp;
:返回修改后的临时对象tmp
,作为减去指定天数后的新日期对象。
代码实现
//.h文件 #pragma once #include<iostream>//流文件 #include<assert.h>//断言头文件 using namespace std;//域展开 class Date { public: //构造函数 Date(int year = 1901, int month = 1, int day = 1); //打印函数 void print(); //运算符重载 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; private: int _year; int _month; int _day; }; //.cpp实现文件 #include"Date.h" //构造函数 Date::Date(int year, int month, int day) :_year(year) , _month(month) , _day(day) {} //打印函数 void Date::print() { cout << _year << "/" << _month << "/" << _day << endl << endl; } //运算符重载 bool Date::operator==(const Date& d)const { return _year == d._year && _month == d._month && _day == d._day; } bool Date::operator<(const Date& d)const { if (this->_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; } 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); } //计算加法 Date& Date::operator+=(int day) { if (day < 0) { return *this -= day; } _day += day; while (_day > GetmonthDay(_year, _month)) { _day -= GetmonthDay(_year, _month); _month += 1; if (_month > 12) { _year += 1; _month = 1; } } return *this; } Date Date::operator+(int day)const { Date tmp = *this; tmp += day;//这里需要注意一个点,不能是this的复用,this的复用会导致自身变化 return tmp; } //日期类的实现-= Date& Date::operator-=(int day) { if (day < 0) { return *this += day; } _day -= day; while (_day <= 0) { _month -= 1; if (_month == 0) { _year -= 1; _month = 12; } _day += GetmonthDay(_year, _month); } return *this; } Date Date::operator-(int day)const { Date tmp = *this; tmp -= day; return tmp; } //测试文件 #include"Date.h" int main() { //日期类构造函数的测试 Date d1(111,1,1); d1.print(); //日期类拷贝构造函数的测试 Date d2(d1); d2.print(); //日期类获取每个月天数的测试 int ret1 = d2.GetmonthDay(1999, 2); cout << ret1 << endl << endl; //比较测试(没有问题) Date d3(1, 1, 1); Date d4(1, 1, 1); Date d5(2, 2, 2); bool ret = d3.operator==(d4); bool ret2 = d3.operator<(d5); bool ret3 = d3.operator<=(d4); bool ret4 = d5.operator<=(d3); bool ret5 = d5.operator<(d3); cout << ret << endl << endl; cout << ret2 << endl << endl; cout << ret3 << endl << endl; cout << ret4 << endl << endl; cout << ret5 << endl << endl; //测试日期类的加等和加 Date d6; d6 += 1000; d6.print(); Date ret6 = d6 + 10000; ret6.print(); //测试日期类的减等和减 Date d7; d7 -= 10000; d7.print(); Date d8; Date ret8 = d8 - 1000; ret8.print(); return 0; }
测试:没有问题
为什么有一个判断是否是负值的判断:
这里提醒一下,规范(+,+=,-,-=)的格式,防止因为传参传递的是负数从而导致计算出现误差,简单的说当是+=的时候传递的数值是负值,我们直接去调用-=
运算符重载++和--的实现
前置和后置的区别
我们以++来说明
一般我们会给后置类型加上一个参数
也就是:
当然这里你也可以传递0或者1
前置++
Date& Date::operator++() { return *this += 1; }
实现逻辑
return *this += 1;
这行代码通过调用已经重载的加法赋值运算符+=
来实现日期的自增。这里将整数 1 作为参数传递给+=
运算符,表示将日期增加一天。*this
表示当前的日期对象,通过调用+=
运算符,在当前日期对象上加上一天。这个操作会修改当前对象的日期状态。- 最后,返回当前日期对象的引用,以便支持连续的自增操作。
后置++
Date Date::operator++(int) { Date tmp = *this; *this += 1; return tmp; }
实现逻辑
Date tmp = *this;
:创建一个临时的Date
对象tmp
,并使用当前对象(*this
)进行初始化。这样就复制了当前对象的状态到tmp
中,保存了自增前的日期对象。*this += 1;
:调用已经重载的加法赋值运算符+=
,在当前对象上加上一天,实现日期的自增操作。return tmp;
:返回临时对象tmp
,即自增前的日期对象副本。这里的关键在于对象本身进行+,但是返回的是创建的临时对象
前置--
Date& Date::operator--() { return *this -= 1; }
实现逻辑
return *this -= 1;
这行代码通过调用已经重载的减法赋值运算符-=
来实现日期的自减。这里将整数 1 作为参数传递给-=
运算符,表示将日期减少一天。*this
表示当前的日期对象,通过调用-=
运算符,在当前日期对象上减去一天。这个操作会修改当前对象的日期状态。- 最后,返回当前日期对象的引用,以便支持连续的自减操作。
后置--
Date Date::operator--(int) { Date tmp = *this; *this -= 1; return tmp; }
实现逻辑
Date tmp = *this;
:创建一个临时的Date
对象tmp
,并使用当前对象(*this
)进行初始化。这样就复制了当前对象的状态到tmp
中,保存了自减前的日期对象。*this -= 1;
:调用已经重载的减法赋值运算符-=
,在当前对象上减去一天,实现日期的自减操作。return tmp;
:返回临时对象tmp
,即自减前的日期对象副本。
计算日期之间相差多少天
这里有坑,这里有坑,这里有坑
首选我们直接上代码,因为代码是很简单的,但是有一些语法结构是有坑的
//日期之间相差多少天 int Date::operator-(const Date& d)const { //这里采取假设法,假设this是大的,如果不是,则进行转换 Date max = *this; Date min = d; int flag = 1; if (*this < d) { max = d; min = *this; flag = -1; } int cout = 0; while (max != min) { ++min; ++cout; } return cout* flag; } //测试 int main() { Date ret6 = d6 + 10000; ret6.print(); //首先,编译器尝试寻找一个可以处理d6 + 10000的操作符重载函数,比如operator + (int), //如果没有找到这样的函数。 //然后,编译器可能会尝试将d6 + 10000解释为d6.operator+(Date(10000)), //也就是先通过单参数构造函数将整数10000转换为一个临时的Date对象, //然后再尝试调用可能存在的operator + (const Date&)成员函数来执行加法操作。 // // 两个解决办法 // 1, explicit Date(int value); //使用explicit关键字后,编译器将不会自动使用这个单参数构造函数进行隐式类型转换,从而避免了意 外的行为。 // 2,加上const修饰 //测试日期类的减等和减 Date d7; d7 -= 10000; d7.print(); Date d8(2000,1,1); Date ret8 = d8 - 1000; //原因是:1000但参构造成为了Date对象,导致调用不明确 禁止掉可以解决折扣的问题 ret8.print(); //如果你给那个函数加上了const就只有const对象可以调用 不会出现调用不明确了 RETURN 0; }
逻辑注意:
- 首先我们实现逻辑的时候,不能真的这个用日期和日期直接相互减减,因为那样会很麻烦
- 所以我们可以直接利用追击问题,当数值一样的时候,我们就可以跳出循环
实现逻辑
- 首先进行大小判断和初始化:
Date max = *this;
和Date min = d;
以及int flag = 1;
这几行代码假设当前对象(*this
)是较大的日期,将其赋值给max
,传入的参数对象d
赋值给min
,并设置标志变量flag
为 1。- 如果当前对象实际上小于传入的对象,即
*this < d
,则进行交换:max = d;
和min = *this;
,同时将标志变量flag
设为 -1。这样确保max
始终是较大的日期,min
始终是较小的日期。- 然后计算日期差:
- 使用一个循环
while (max!= min)
,只要两个日期不相等就继续循环。- 在循环中,每次将较小的日期
min
递增一天(这里假设Date
类实现了自增运算符++
,使其能够正确地增加一天,同时处理月份和年份的变化),并将天数计数器cout
加一。- 当两个日期相等时,循环结束。
- 最后返回结果:
- 将天数计数器
cout
乘以标志变量flag
,得到最终的日期差。如果一开始假设正确,即当前对象较大,那么返回正数的日期差;如果当前对象较小,返回负数的日期差。这里有坑的点:
所以还是回到那一句话,const应加尽加
解决办法一:使用 explicit 关键字
在单参数构造函数上面加上
explicit
关键字,例如explicit Date(int value)
。使用explicit
关键字后,编译器将不会自动使用这个单参数构造函数进行隐式类型转换,从而避免可能出现的调用歧义。解决办法二:对成员函数进行 const 修饰
对于
int operator-(const Date& d) const;
这个成员函数加上const
修饰。这里加上const
修饰可以解决调用歧义的原因是:这个函数的返回值是int
类型,也就是常量。常量具有不可修改的特性,所以就不会导致调用歧义。
C++流插入(输出到屏幕上面)和流提取(输入)
方式1:重载为成员函数
这里解释一下,重载为成员函数是可以的,但是是不符合我们的使用习惯,我这里用图解解释一下,了解就可以,逻辑思路都一样
方式2(使用):友元函数
这里是比较多的使用方式,友元函数也是比较简单的,只需要再函数的公有区域加上
friend+函数全称;
流插入(输出)(重载为全局函数):
//流插入(输出到屏幕)//给返回值,支持连续赋值,不然只能一次打印一个 ostream& operator<<(ostream& out, const Date& d) { out << d._year << "年" << d._month << "月" << d._day << "日" << endl << endl; return out; }
注意
- 这里的out是cout的简称
- 这里我们需要有返回值的目的是,支持链式操作,也就是输入可以连续输入,输出可以连续输出,没有返回值只能实现单次
- 有返回值能实现链式操作,因为对于赋值运算而言赋值是从右往左实现的,但是对于插入流而言是从左往右实现的 ,每次都会返回cout从而进行下一次的操作
实现逻辑
out << d._year << "年" << d._month << "月" << d._day << "日" << endl << endl;
这行代码将日期对象d
的年份、月份和日期按照特定格式输出到输出流out
中。
- 首先输出年份
d._year
,并紧跟一个 “年” 字。- 接着输出月份
d._month
,并紧跟一个 “月” 字。- 然后输出日期
d._day
,并紧跟一个 “日” 字。- 最后输出两个换行符
endl << endl
,用于在输出日期后进行换行,使输出更加清晰。return out;
返回输出流对象的引用,以便支持连续输出操作。
流提取(输入)(重载为全局函数):
代码实现:
//流提取(提取输入到类里面,所以类不能加const) istream& operator>>(istream& in, Date& d) { in >> d._year >> d._month >> d._day; return in; }
注意:
- 这里的传递过来的对象不能加上const,因为输入本身就是需要改变对象的,所以是不能加上const的
实现逻辑
in >> d._year >> d._month >> d._day;
这行代码从输入流in
中依次读取数据,并分别赋值给Date
对象d
的年份、月份和日期成员变量。
- 首先读取一个整数作为年份,存储到
d._year
中。- 接着读取一个整数作为月份,存储到
d._month
中。- 最后读取一个整数作为日期,存储到
d._day
中。return in;
返回输入流对象的引用,以便支持连续输入操作。
日期类的检查
//这里我们直接重载为内联函数 //检查日期是否合法 bool CheckDate() { //检查月份,天数 if (_year < 0 || _month < 1 || _month>12 || _day < 1 || _day>this->GetmonthDay(_year, _month)) { return false; } else { return true; } }
所以我们可以在哪检查,可以在输入的时候和构造的时候检查,这里我只是在提取流进行检查
//流提取(提取输入到类里面,所以类不能加const) istream& operator>>(istream& in, Date& d) { while (1) { cout << "请依次输入年月日(只支持公元前的写法):"; in >> d._year >> d._month >> d._day; if (!d.CheckDate()) { cout << "输入的日期类不合法,请重新输入:"; d.print(); } else { break; } } return in; }
实现逻辑
while (1)
:这是一个无限循环,目的是不断要求用户输入合法的日期,直到输入正确为止。cout << "请依次输入年月日(只支持公元前的写法):";
:提示用户输入日期信息,并明确只支持公元前的写法。in >> d._year >> d._month >> d._day;
:从输入流中读取三个整数,分别存储到Date
对象d
的成员变量_year
(年份)、_month
(月份)和_day
(日期)中。if (!d.CheckDate())
:调用Date
类的成员函数CheckDate()
来检查输入的日期是否合法。如果这个函数返回false
,说明输入的日期不合法。
cout << "输入的日期类不合法,请重新输入:";
:如果日期不合法,输出提示信息,要求用户重新输入。d.print();
:可能是输出当前输入的不合法日期,以便用户查看并纠正错误。else
:如果日期合法。
break;
:跳出无限循环。
代码总结
//Date.h头文件
#pragma once #include<iostream>//流文件 #include<assert.h>//断言头文件 using namespace std;//域展开 class Date { //友元函数(输入输出流) friend ostream& operator<<(ostream& out, const Date& d); friend istream& operator>>(istream& in, Date& d); public: //构造函数 Date(int year = 1901, int month = 1, int day = 1); //拷贝构造函数 Date(const Date& d); //获取一个日期(内联函数) int GetmonthDay(int year, int month)const { assert(month < 13 && month>0); static int monthDayArray[13] = { -1,31,28,31,30,31,30,31,31,30,31,30,31 }; //四年一润,百年不润。四百年一润。 if ((month == 2) && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))) { return 29; } else { return monthDayArray[month]; } } //检查日期是否合法 bool CheckDate() { //检查月份,天数 if (_year < 0 || _month < 1 || _month>12 || _day < 1 || _day>this->GetmonthDay(_year, _month)) { return false; } else { return true; } } //运算符重载 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; //++ -- Date& operator++(); Date operator++(int); Date& operator--(); Date operator--(int); //打印函数 void print(); //日期之间相差多少天 int operator-(const Date& d)const;//这里加上const,就不会因为单参数构造产生调用歧义 private: int _year; int _month; int _day; };
//Date.cpp实现文件
#include"Date.h" //构造函数 Date::Date(int year, int month, int day) :_year(year) , _month(month) , _day(day) {} //拷贝构造函数 Date::Date(const Date& d) { _year = d._year; _month = d._month; _day = d._day; } //打印函数 void Date::print() { cout << _year << "/" << _month << "/" << _day << endl << endl; } //运算符重载 bool Date::operator==(const Date& d)const { return _year == d._year && _month == d._month && _day == d._day; } bool Date::operator<(const Date& d)const { if (this->_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; } 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 !(*this == d); } //计算加法 Date& Date::operator+=(int day) { if (day < 0) { return *this -= day; } _day += day; while (_day > GetmonthDay(_year, _month)) { _day -= GetmonthDay(_year, _month); _month += 1; if (_month > 12) { _year += 1; _month = 1; } } return *this; } Date Date::operator+(int day)const { Date tmp = *this; tmp += day;//这里需要注意一个点,不能是this的复用,this的复用会导致自身变化 return tmp; } //日期类的实现-= Date& Date::operator-=(int day) { if (day < 0) { return *this += day; } _day -= day; while (_day <= 0) { _month -= 1; if (_month == 0) { _year -= 1; _month = 12; } _day += GetmonthDay(_year, _month); } return *this; } Date Date::operator-(int day)const { Date tmp = *this; tmp -= day; return tmp; } //前置++和后置++ Date& Date::operator++() { return *this += 1; } Date Date::operator++(int) { Date tmp = *this; *this += 1; return tmp; } //前置--和后置-- Date& Date::operator--() { return *this -= 1; } Date Date::operator--(int) { Date tmp = *this; *this -= 1; return tmp; } //日期之间相差多少天 int Date::operator-(const Date& d)const { //这里采取假设法,假设this是大的,如果不是,则进行转换 Date max = *this; Date min = d; int flag = 1; if (*this < d) { max = d; min = *this; flag = -1; } int cout = 0; while (max != min) { ++min; ++cout; } return cout* flag; } //流插入(输出到屏幕)//给返回值,支持连续赋值,不然只能一次打印一个 ostream& operator<<(ostream& out, const Date& d) { out << d._year << "年" << d._month << "月" << d._day << "日" << endl << endl; return out; } //流提取(提取输入到类里面,所以类不能加const) istream& operator>>(istream& in, Date& d) { while (1) { cout << "请依次输入年月日(只支持公元前的写法):"; in >> d._year >> d._month >> d._day; if (!d.CheckDate()) { cout << "输入的日期类不合法,请重新输入:"; d.print(); } else { break; } } return in; }
//test.cpp测试文件
#include"Date.h" int main() { //日期类构造函数的测试 Date d1(111,1,1); d1.print(); //日期类拷贝构造函数的测试 Date d2(d1); d2.print(); //日期类获取每个月天数的测试 int ret1 = d2.GetmonthDay(1999, 2); cout << ret1 << endl << endl; //比较测试(没有问题) Date d3(1, 1, 1); Date d4(1, 1, 1); Date d5(2, 2, 2); bool ret = d3.operator==(d4); bool ret2 = d3.operator<(d5); bool ret3 = d3.operator<=(d4); bool ret4 = d5.operator<=(d3); bool ret5 = d5.operator<(d3); cout << ret << endl << endl; cout << ret2 << endl << endl; cout << ret3 << endl << endl; cout << ret4 << endl << endl; cout << ret5 << endl << endl; //测试日期类的加等和加 Date d6; d6 += 1000; d6.print(); Date ret6 = d6 + 10000; ret6.print(); //首先,编译器尝试寻找一个可以处理d6 + 10000的操作符重载函数,比如operator + (int), //如果没有找到这样的函数。 //然后,编译器可能会尝试将d6 + 10000解释为d6.operator+(Date(10000)), //也就是先通过单参数构造函数将整数10000转换为一个临时的Date对象, //然后再尝试调用可能存在的operator + (const Date&)成员函数来执行加法操作。 // // 两个解决办法 // 1, explicit Date(int value); //使用explicit关键字后,编译器将不会自动使用这个单参数构造函数进行隐式类型转换,从而避免了意外的行为。 // 2,加上const修饰 // //测试日期类的减等和减 Date d7; d7 -= 10000; d7.print(); Date d8(2000,1,1); Date ret8 = d8 - 1000; //原因是:1000但参构造成为了Date对象,导致调用不明确 禁止掉可以解决折扣的问题 ret8.print(); //如果你给那个函数加上了const就只有const对象可以调用 不会出现调用不明确了 //测试日期类的++和-- Date d9; Date ret9 = d9--; ret9.print(); ++d9; ++d9; d9.print(); //流插入测试 cout << d9; //流提取测试 Date d10; cin >> d10; cout << d10; //测试日期之间相差多少天 Date d11(1901, 1, 1); Date d12(2000, 3, 1); int ret10 = d11 - d12; cout << ret10 << endl; return 0; }