【C++】--- 类和对象(中)之日期类的实现
日期类的实现
1. 应该实现哪些默认成员函数
构造函数是需要自己来实现的,因为日期类的成员变量都是内置类型,是否初始化取决于编译器,这里可以给出一个带参全缺省的构造函数,由于日期类不需要申请资源,所有不用显式的实现析构函数,同时由于不需要申请资源并且成员变量都是内置类型,默认的拷贝构造函数和赋值运算符重载函数的浅拷贝可以满足我们的需求,所有只需要实现一个构造函数即可。
class Date
{
public:
Date(int year = 2025, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
2. 日期类比较大小的六个运算符重载
bool operator<(const Date& d);
bool operator>(const Date& d);
bool operator<=(const Date& d);
bool operator>=(const Date& d);
bool operator==(const Date& d);
bool operator!=(const Date& d);
形参设置为const引用的原因是:
- 日期对象的比较不需要改变日期对象
- 引用可以减少拷贝
- 可以接收const类型的对象防止权限缩小的问题发生
当没有修改对象的需求的时候,建议形参使用const引用。
2.1 operator==
这个函数的实现是非常简单的
bool Date::operator==(const Date& d)
{
return _year == d._year
&& _month == d._month
&& _day == d._day;
}
2.2 operator<
bool Date::operator<(const Date& d)
{
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;
}
return false;
}
2.3 剩下的4个重载函数
前面我们实现了 == 和 < 的运算符重载,剩下的四个我们可以使用函数复用的方式来实现。
bool Date::operator!=(const Date& d)
{
return !(*this == d);
bool Date::operator>(const Date& d)
{
return !(*this <= d);
}
bool Date::operator<=(const Date& d)
{
return *this < d || *this == d ;
bool Date::operator>=(const Date& d)
{
return !(this < d);
}
3. 日期类的加减运算符重载
Date& operator+=(int day);
Date operator+(int day);
Date& operator-=(int day);
Date operator-(int day);
这里的思路也是实现 operator+= 和operator -= ,operator+ 和opeartor- 分别复用即可。
在这之前需要先实现一个GetMonthDay函数来获取月份
int GetMonthDay(int year, int month);
{
assert(month > 0 && month < 13);
static int monthArray[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 monthArray[month];
}
}
- 由于调用日期类加减运算符重载的时候这个函数会被频繁的调用,所以这个函数可以直接写在日期类中,因为日期类的函数默认是inline
- monthArray设置为静态的,在整个程序生命周期中只需创建一次即可。
3.1 operator+=
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)
{
Date tmp = *this //拷贝构造
tmp += day;
return tmp;
这里的返回值不能使用引用,因为tmp出了operator+的作用域就销毁了。
3.2 operator-= 和 opeartor-
Date Date::operator-=(int day)
{
//if (day < 0)
//{
// return *this += -day;
//}
_day -= day;
while (_day <= 0 )
{
_month--;
_day += GetMonthDay(_year, _month);
if (_month == 0)
{
_month = 12;
_year--;
}
}
return *this;
}
Date Date::operator-(int day)
{
Date temp = *this;
temp -= day;
return temp;
}
4. 前后置++和前后置- -的运算符重载
Date& operator++();
Date operator++(int);
Date& operator--();
Date operator--(int);
可以直接复用日期类的加减运算符重载
4.1 前后置++
Date& Date::operator++()
{
*this += 1;
return *this;
}
Date operator++(int)
{
Date tmp = *this;
tmp += 1;
return tmp;
4.2 前后置–
Date& Date::operator--()
{
*this -= 1;
return *this;
}
Date Date::opeartor--(int)
{
Date tmp = *this;
tmp -= 1;
return tmp;
5. operator-(const Date& d)
这里是日期类和日期类相减,是operator-(int day)
的一个函数重载
int Date::operator-(const Date& d)
{
Date max = *this;
Date min = d;
flag = 1;
if(d > *this)
{
max = d ;
min = *this;
flag = 1;
}
int count = 0;
while(min<max)
{
min++;
count++;
}
return flag*count;
}
这里通过循环来计数的方式是很不错,避开了复杂的年月到日的转换。
6. << 和 >> 运算符的重载
前面的函数可以重载为类成员函数,但是这两个函数例外需要重载为全局函数。
我们先把<<重载为类成员函数看看效果
ostream& Date::operator<<(ostream& out)
{
out << d1._year << "年" << d1._month << "月" << d1._day << "日" << endl;
return out;
调用类中成员函数<<的效果为
int main()
{
Date d1(2020,1,1);
d1 << cout ;
return 0;
}
这样看起来很奇怪和我们平时使用 << 的方式相反,所以 << 和 >> 需要被重载为全局函数,下面给出两个函数的声明
ostream& operator<<(ostream& out,const Date& d1);
istream& operator>>(istream& in, Date& d);
<< 的参数 out 和 >>的参数 in 、d 不需要const引用的原因是这三个对象都需要被修改。
6.1 << 运算符的重载实现
ostream& Date::operator<<(ostream& out, const Date& d)
{
out << d._year << "年" << d._month << "⽉" << d._day << "⽇" << endl;
return out;
}
返回值不为空为对象引用的原因是这样可以实现链式插入
cout<<d1<<d2<<endl;
cout<<d1
执行完后返回cout对象继续执行cout<<d2
,当然>>重载运算符的实现返回值为引用也是这个原因。
6.2 >> 运算符重载函实现
istream& operator>>(istream& in, Date& d)
{
cout << "请依次输⼊年⽉⽇:>";
in >> d._year >> d._month >> d._day;
return in;
}
这两个函数都是全局函数,无法使用日期类中私有的成员变量,这里我们使用友元来解决这个问题,友元会在面向对象下具体讲解。
friend ostream& operator<<(ostream& out, const Date& d);
friend istream& operator>>(istream& in, Date& d);
即把函数的声明前加关键字friend
后放在类中的任意位置,这样这两个函数就可以使用日期类的私有成员了。
7. 日期类的完善
7.1checkDate函数
checkDate函数主要是用来检查我们输入的日期是否违法。
bool Date::CheckDate() const
{
if (_month < 1 || _month > 12
|| _day < 1 || _day > GetMonthDay(_year, _month))
{
return false;
}
else
{
return true;
}
}
这个函数可以放在构造函数中和>>重载函数中来健壮程序。
Date::Date(int year,int month ,int day)
{
_year = year;
_month = month;
_day = day;
if (!CheckDate())
{
cout << "日期非法->";
cout << *this;
}
}
istream& operator>>(istream& in, Date& d)
{
while (1)
{
in >> d._year >> d._month >> d._day;
if (d.CheckDate())
{
break;
}
else
{
cout << "日期非法,请重新输入" << endl;
}
}
return in;
}
7.2 const成员函数
这里使用const修饰成员函数中的this指针指向的内容,添加const的原则是应加尽加,即不修改对象本身的函数都可以加const修饰
void print() 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;
bool operator!= (const Date& d)const;
bool CheckDate() const;
int GetMonthDay(int year, int month) const
Date operator+(int day)const;
Date operator-(int day)const;
int operator-(const Date& d)const;
上面的成员函数都是不改变对象本身的,可以加const修饰
Date(int year = 1, int month = 1, int day = 1);
Date& operator+=(int day);
Date& operator-=(int day);
Date& operator++();
Date operator++(int);
Date& operator--();
Date operator--(int);
这些函数是修改成员变量的,不加const。
另外<<和>>的重载函数由于重载为全局函数,也不用加const,const修饰的是成员函数中被隐藏的this指针指向的内容。