有了前面学到的类和对象-1、类和对象-2、类和对象-3的知识,我们就可以尝试写一个功能较为完善的日期类。
首先先给出日期类的声明:
class Date
{
public:
// 获取某年某月的天数
int GetMonthDay(int year, int month);
// 全缺省的构造函数
Date(int year = 1900, int month = 1, int day = 1);
// 拷贝构造函数
// d2(d1)
Date(const Date& d);
// 赋值运算符重载
// d2 = d3 -> d2.operator=(&d2, d3)
Date& operator=(const Date& d);
// 析构函数
~Date();
// 日期+=天数
Date& operator+=(int day);
// 日期+天数
Date operator+(int day);
// 日期-天数
Date operator-(int day);
// 日期-=天数
Date& operator-=(int day);
// 前置++
Date& operator++();
// 后置++
Date operator++(int);
// 后置--
Date operator--(int);
// 前置--
Date& operator--();
// >运算符重载
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);
// 日期-日期 返回天数
int operator-(const Date& d);
private:
int _year;
int _month;
int _day;
};
我们这里的构造函数为全缺省构造,所以要手动实现一下。当然这并不困难,甚至对于依然身经百战的我们来说这简直就是轻而易举的:
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
然后由于我们的日期类的属性都是内置类型,所以没必要手动实现析构、拷贝构造和赋值重载了。
~Date() = default;
Date(const Date& d) = default;
Date& operator=(const Date& d) = default;
由于每个月的天数参差不齐和闰年的存在,手码一个函数来或者某个月的天数还是挺有必要的:
int Date::GetMonthDay(int year, int month)
{
static int arr[12] = { 31,28,31,30,31,30,31,31,30,31,30,31 };
if (month == 2 && (year % 400 == 0 || (year % 4 == 0 && year % 100 != 0)))
{
return 29;
}
return arr[month - 1];
}
首先我们先写一个<和==的重载函数:
bool Date::operator < (const Date& d)
{
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)
{
return _year == d._year && _month == d._month && _day == d._day;
}
那么我们就可以根据上面实现的代码来重载其余的运算符:
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);
}
bool Date::operator >= (const Date& d)
{
return !(*this < d);
}
这样比起自己手码实现是不是方便了很多呢?当然剩下部分函数的实现也会用到类似的方法哦!
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;
tmp = tmp += day;
return tmp;
}
注意到:
当然是可以的,但是不支持这样实现。因为重载+的返回值是一个临时变量,如果+=是利用+来实现的又会生成一个临时变量。而如果我们先实现+=的重载就不需要生成临时变量。因此这种实现方式效率较低。
有了前面的逻辑,那么后面我们实现-=和-就简单了:
Date& Date::operator-=(int day)
{
_day -= day;
while (_day < 1)
{
--_month;
if (_month == 0)
{
--_year;
_month = 12;
}
_day += GetMonthDay(_year, _month);
}
return *this;
}
Date Date::operator-(int day)
{
Date tmp = *this;
tmp = tmp -= day;
return tmp;
}
在重载前置++和后置++的时候,我们不得不思考一个问题:前置++和后置++的符号、参数都相同所以不能构成函数重载,那要怎样才能区分他们呢?
事实上按照前面的语法这确实区分不了,因此c++规定后置++的参数要多一个int。由此就能区分他们了:
Date& Date::operator++()
{
(*this) += 1;
return *this;
}
Date Date::operator++(int)
{
Date tmp = *this;
tmp += 1;
return tmp;
}
由此,我们又需要注意几个问题:
Date d1;
d1.operator++();//前置++
d1.operator++(0);//后置++
那么接下来我们继续实现前置–和后置–:
Date& Date::operator--()
{
(*this) -= 1;
return *this;
}
Date Date::operator--(int)
{
Date tmp = *this;
tmp -= 1;
return tmp;
}
两个日期类相减并返回其天数。
有了我们前面的基础,这个代码并不难实现:
int Date::operator-(const Date& d)
{
Date max = *this;
Date min = d;
int p = 1;
if (max < min)
{
max = d;
min = *this;
p = -1;
}
int day = 0;
while (max != min)
{
++day;
++min;
}
return p * day;
}
需要注意的小细节有,我们判定循环条件不是max>min而是max!=min。
这是因为!=的实现逻辑更简单,效率也更高。
如果我们想要打印日期类,用如下方式是不可行的:
Date d1;
cout<<d1<<endl;
因为cout不能打印自定义类型,那么我们就可以重载<<来实现cout打印自定义类型。
首先要知道cout是ostream的一个类,因此实现如下:
ostream& Date::operator<<(ostream& out)
{
out << _year << '/' << _month << '/' << _day << endl;
return out;
}
上面的实现好像没什么大问题,不过嘛。
咳咳,由于this指针的存在,操作符重载默作为成员函数时,第一个参数默认为this,并且不可修改。所以用起来就像这样:
Date d1(2024,3,21);
d1<<cout;
好家伙,本来应该时cout<
ostream& operator<<(ostream& out, const Date& d)
{
out << d._year << '/' << d._month << '/' << d._day << endl;
return out;
}
不过嘛,这样又有一个问题:类的属性一般是私有的,所以我们访问不了。这时候我们就可以通过友元来解决这个问题。
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
friend ostream& operator<<(ostream& out, const Date& d);
private:
int _year;
int _month;
int _day;
};
这样我们就能cout自定义类型啦!
int main()
{
Date d1(2024, 3, 21);
Date d2(2024, 8, 1);
cout << d1 << d2 << endl;
return 0;
}
istream& operator>>(istream& in, Date& d)
{
in >> d._year >> d._month >> d._day;
return in;
}
那么以上就是本篇文章的全部内容,希望对读者复习巩固类和对象有所帮助。