大家好,我是苏貝,本篇博客带大家了解C++的实现日期类,如果你觉得我写的还不错的话,可以给我一个赞👍吗,感谢❤️
目录
- 1 ==/!=/>/</>=/<=运算符重载
- 2 +/-/+=/-=运算符重载
- (A) 先写+,再通过+写+=
- (B) 先写+=,再通过+=写+
- (C) 先写-,再通过-写-=
- (D) 先写-=,再通过-=写-
- 3 前置/后置++/--
- 4 日期-日期
- 5 >>/<<运算符重载
- (A) <<
- (B) >>
- 6 优化
- (A) const
- (B) 判断日期是否有效
- 7.模块化代码实现
- 1. Date.h
- 2. Date.cpp
- 3. test.cpp
日期类需要自主实现构造函数,运算符重载(>,<,>=,<=,==,!=,+,-,+=,-=,++),不需要自主实现析构函数(没有动态开辟空间)、拷贝构造函数(成员变量中没有自定义类型的)。经过前面的学习,我们可以比较快的写出Date类的大部分
现在我们来新建3个文件Date.h、Date.cpp、test.cpp
将Date类放在Date.h文件中
上述函数的声明写在类中,但实现写在Date.cpp中
构造函数用全缺省的,注意:缺省参数如果声明和定义分离,那么缺省参数写在函数声明,函数定义中不写。因为声明和定义分离,所以函数定义时函数名前需要加Date::
1 ==/!=/>/</>=/<=运算符重载
2 +/-/+=/-=运算符重载
我们有2种写法,一是先写+,再通过+写+=。二是先写+=,再通过+=写+
这里的+/-是+/-天数,如2022年3月4日加10天=2022年3月14日
(A) 先写+,再通过+写+=
在实现+运算符重载前,我们要得到每个月的天数。可以定义一个函数,用if/else条件判断语句来返回天数,当然,这比较麻烦。最简单的方法是定义一个数组
上面的函数可以优化一下,将数组用static修饰,这样数组就不在栈区。以后就不用每调用一次GetMonthDay函数,就要定义一个数组,等到函数运行完成,还要销毁数组了。
问:下面的Date tmp=this;是拷贝构造还是赋值运算符重载?拷贝构造,因为是用已存在的类类型对象this创建新对象tmp
为什么+运算符重载返回值类型是Date,+=运算符重载返回值类型是Date&?
如果可以,我们都希望返回的是Date&类型的,因为这返回的是引用。如果返回的是Date类型的,那么返回的是返回值的拷贝。
Tmp是临时变量,临时变量出了作用域就销毁了,所以临时变量不能用引用返回,所以返回值类型是Date。下面的返回值类型同理
(B) 先写+=,再通过+=写+
问:上面的2种方法写+和+=,哪一种更好?
右边的更好,因为左边的+=里复用+需要创建对象,右边的+里复用+=不需要创建对象
© 先写-,再通过-写-=
(D) 先写-=,再通过-=写-
同理,-/-=中先写-=再通过-=写-更好
3 前置/后置++/–
4 日期-日期
这和-天数是函数重载
思路:
先知道哪个的日期较大/较小,让较小的日期进入循环,每循环一次,较小的日期+1,等到两日期相等,看循环的次数就知道两日期相差几天
5 >>/<<运算符重载
(A) <<
我们知道,<<(流插入运算符)支持内置类型的打印,但不支持自定义类型的打印
那我们想实现<<打印自定义类型怎么办?自己写一个<<的运算符重载
将<<运算符重载的声明写在类中,<<运算符重载需要有参数:cout(类型是ostream),为什么?因为<<是双操作符,因此需要2个形参,形参this隐含,所以还需要cout当实参
为什么cout<<d1;会报错?因为双操作数的操作符,左右的操作数必须和运算符重载函数的形参顺序相同,函数的第一个形参是隐藏的this,第二个才是out。因此可以d1<<cout,而不能cout<<d1。
为解决这个问题,我们就要是out是第一个形参,this是第二个形参,可是这在类中的成员函数中是不可能的。因此<<运算符重载不能写在类里,要写在全局域中。
注意:全局函数(如函数a)的定义不能写在.h文件中。如果全局函数的定义写在.h文件中,因为.h文件被2个.cpp文件包含,所以预处理时2个.cpp文件都会展开.h文件,这样就会有2个a,链接时会报错。
因此<<运算符重载也要声明和定义分离
Date类的成员变量是private修饰的,即不能在类外被访问。所以上图会报错,如何解决?
方法1:在Date类里写GetYear/GetMonth/GetDay函数
方法2:友元声明
友元声明即在类中也写一下函数的声明,且在声明前+friend(全局域里也需要有函数的声明,因此这个函数的声明有2个)
完成上面的要求,就能让<<打印自定义类型
在C++中,<<是可以连续使用的,但我们实现的不可以,为什么?
因为我们相等<<运算符重载没有返回cout,如果返回了cout,那么第一次cout<<d1后的返回值是cout,那接下来的表达式为cout<<d2<<endl。因此修改<<运算符重载的返回值,就能连续使用<<
(B) >>
Cin的类型是istream。>>和<<运算符重载一样,都需要在类中有友元声明
6 优化
(A) const
在Date类的成员函数中,有许多是不修改*this的,所以这些函数的this指针可以被const修饰
(B) 判断日期是否有效
像下图,2月30日是不可能的情况,所以我们需要对输入的日期进行检查
在哪里需要检查日期的有效性呢?需要输入日期的地方:构造函数和>>运算符重载
构造函数:
运算符>>重载:
7.模块化代码实现
1. Date.h
#pragma once
#include<iostream>
using std::endl;
using std::cout;
using std::cin;
using std::istream;
using std::ostream;
class Date
{
public:
bool CheckValid();
Date(int year = 2020, int month = 1, int day = 1);
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) const;
Date& operator+=(int day);
Date operator-(int day) const;
Date& operator-=(int day);
Date& operator++();
Date operator++(int);
Date& operator--();
Date operator--(int);
int operator-(const Date& d);
friend ostream& operator<<(ostream& out, const Date& d);
friend istream& operator>>(istream& in, Date& d);
int GetMonthDay(int year, int month)
{
static int monthday[13] = { 0,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;
}
return monthday[month];
}
void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
ostream& operator<<(ostream& out, const Date& d);
istream& operator>>(istream& in, Date& d);
2. Date.cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include"Date.h"
Date::Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
if (!CheckValid())
{
cout << "构造日期错误" << _year << "-" << _month << "-" << _day << 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
{
return !(*this == d);
}
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 || *this > d;
}
bool Date::operator<=(const Date& d) const
{
return !(*this > d);
}
Date Date::operator+(int day) const
{
Date tmp(*this);
tmp += day;
return tmp;
}
Date& Date::operator+=(int day)
{
_day += day;
while (_day > GetMonthDay(_year, _month))
{
_day -= GetMonthDay(_year, _month);
_month++;
if (_month == 13)
{
_year++;
_month = 1;
}
}
return *this;
}
//Date Date::operator+(int day)
//{
// Date tmp(*this);//拷贝构造
// //Date tmp = *this;//拷贝构造
// tmp._day += day;
// while (tmp._day > GetMonthDay(tmp._year, tmp._month))
// {
// tmp._day -= GetMonthDay(tmp._year, tmp._month);
// tmp._month++;
// if (tmp._month == 13)
// {
// tmp._year++;
// tmp._month = 1;
// }
// }
// return tmp;
//}
//
//Date& Date::operator+=(int day)
//{
// *this = *this + day;
// 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;
}
//Date Date::operator-(int day)
//{
// Date tmp(*this);
// tmp._day -= day;
// while (tmp._day <= 0)
// {
// tmp._month--;
// if (tmp._month == 0)
// {
// tmp._year--;
// tmp._month = 12;
// }
// tmp._day += GetMonthDay(tmp._year, tmp._month);
// }
// return tmp;
//}
//
//Date& Date::operator-=(int day)
//{
// *this = *this - day;
// return *this;
//}
//++d
Date& Date::operator++()
{
*this += 1;
return *this;
}
//d++
Date Date::operator++(int)
{
Date tmp(*this);
*this += 1;
return tmp;
}
//--d
Date& Date::operator--()
{
*this -= 1;
return *this;
}
//d--
Date Date::operator--(int)
{
Date tmp(*this);
*this -= 1;
return tmp;
}
//d1-d2
int Date::operator-(const Date& d)
{
Date max(*this);
Date min(d);
int flag = 1;
if (min > max)
{
flag = -1;
max = d;
min = *this;
}
int tmp=0;
while (min != max)//比min<max要简单
{
++min;
tmp++;
}
return tmp * flag;
}
ostream& operator<<(ostream& out, const Date& d)
{
out << d._year << "/" << d._month << "/" << d._day << endl;
return out;
}
istream& operator>>(istream& in, Date& d)
{
cout << "请依次输入年月日:";
while (1)
{
in >> d._year >> d._month >> d._day;
if (!d.CheckValid())
{
cout << "输入的日期错误,请重新输入:";
}
else
break;
}
return in;
}
bool Date::CheckValid()
{
if (_year <= 0
|| _month > 12 || _month <= 0
|| _day > GetMonthDay(_year, _month))
{
return false;
}
else
return true;
}
3. test.cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include"Date.h"
int main()
{
Date d1(2022, 2, 30);
cout << d1 << endl;
/*d1.operator<<(cout);
cout << d1;
d1 << cout;*/
/*cin >> d1;
cout << d1 << endl;*/
/*d3 = d1--;
cout << d3;
d3 = --d2;
cout << d3;*/
//cin >> d3;
/*cin >> d1 >> d2 >> d3;
cout << d1 << d2 << d3;*/
//cout << d1;
/*d1.operator<<(cout);
d1 << cout;*/
/*cout << (d1 - d2) << endl;
cout << (d2 - d1) << endl;*/
/*d3 = ++d1;
d3.Print();
d3 = d2++;
d3.Print();*/
/*d3 = d1 + 49;
d3.Print();
d3= d1 - 342;
d3.Print();
d2 += 67;
d2.Print();
d2 -= 98;
d2.Print();*/
/*d2 = d1;
cout << (d1 == d2) << endl;
cout << (d1 != d2) << endl;
cout << (d1 > d2) << endl;
cout << (d1 >= d2) << endl;
cout << (d1 < d2) << endl;
cout << (d1 <= d2) << endl;*/
return 0;
}
好了,那么本篇博客就到此结束了,如果你觉得本篇博客对你有些帮助,可以给个大大的赞👍吗,感谢看到这里,我们下篇博客见❤️