文章目录
前言
本文是对之前学习知识的小小总结实践。
分析
功能分析:
- 日期类简单的记录我们的日期时间,包括年、月、日
- 支持日期类对象之间的拷贝赋值
- 两日期类对象相减得到天数差
- 日期类对象减特定天数得到新日期
- 日期类对象支持cout打印
- 日期类对象支持cin输入赋值
- 日期类对象之间的各种相互比较的运算符
- 日期类对象的自增、自减
- 获得日期类对象某年某月的有多少天
- 简单打印日志类时间
cpp
class Date
{
// 友元函数声明
friend ostream& operator<<(ostream& out, const Date& d);
friend istream& operator>>(istream& in, Date& d);
public:
//日期类构造函数:
Date(int year = 1, int month = 1, int day = 1);
//其他的一些重载,TODO..
//简单打印日期类时间
void Print() const;
//日期类之间比较的运算符
bool operator<(const Date& x) const;
bool operator==(const Date& x) const;
bool operator<=(const Date& x) const;
bool operator>(const Date& x) const;
bool operator>=(const Date& x) const;
bool operator!=(const Date& x) const;
int GetMonthDay(int year, int month);
// 日期类加天数运算符
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);
//日期类减日期类的天数差
int operator-(const Date& d) const;
private:
int _year;
int _month;
int _day;
};
ostream& operator<<(ostream& out, const Date& d);
istream& operator>>(istream& in, Date& d);
我们实现的是一个简单的日期类,它的不涉及资源的管理,所以一些资源拷贝、复制、销毁的关键成员函数,像:析构、拷贝、赋值重载,我们都无需手动实现,而是交给编译器自动生成,自动生成的浅拷贝已经可以满足。
我们需要手动写的,就是特定的初始化方式,即构造函数,还有一些日期之间的常见运算对应的运算符重载。我们就采用类声明定义分离的方式去实现。
Date.h
cpp
#include<iostream>
#include<cassert>
using std::ostream;
using std::istream;
using std::cout;
using std::cin;
using std::endl;
class Date
{
// 友元函数声明
friend ostream& operator<<(ostream& out, const Date& d);
friend istream& operator>>(istream& in, Date& d);
public:
//日期类构造函数:
Date(int year = 1, int month = 1, int day = 1);
//其他的一些重载,TODO..
//简单打印日期类时间
void Print() const;
//日期类之间比较的运算符
bool operator<(const Date& x) const;
bool operator==(const Date& x) const;
bool operator<=(const Date& x) const;
bool operator>(const Date& x) const;
bool operator>=(const Date& x) const;
bool operator!=(const Date& x) const;
int GetMonthDay(int year, int month);
// 日期类加天数运算符
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);
//日期类减日期类的天数差
int operator-(const Date& d) const; //日期减日期也并不会修改对象本身的值,所以加上const禁止函数内部修改本身的值.
private:
int _year;
int _month;
int _day;
};
ostream& operator<<(ostream& out, const Date& d);
istream& operator>>(istream& in, Date& d);
Date.cpp
cpp
#include"Date.h"
//实现一下构造出对象需求的基本成员函数.
Date::Date(int year, int month, int day)
{
//首先我们需要判断日期的合法性.也因此,我们并不将赋初值的操作放在初始化列表.
if ((month > 0 && month <= 12) && (day > 0 && day <= GetMonthDay(year, month))) //根据不同年份,2月的天数略有差距,所以写一个函数,更好的判断我们的日期天数,当给的数值合法,我们才正常的构造.
{
_year = year;
_month = month;
_day = day;
}
else
{
cout << "非法日期:" << "year=" << year << ", month=" << month << ", day=" << day << endl;
assert(false);
}
}
int Date::GetMonthDay(int year, int month)
{
int DayArr[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
int day = DayArr[month];
if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)) //如果是2月并且是闰年,那么将天数加一.
{
day++;
}
return day;
}
//早早写出打印的函数,可以阶段性的测试我们的代码正确性.
void Date::Print() const
{
cout << "该对象数据:" << "_year=" << _year << ", _month=" << _month << ", _day=" << _day << endl;
}
//2.然后先实现operator==和operator<,为什么?---后面的运算符重载可以复用逻辑.
bool Date::operator==(const Date& x) const //首先这个函数逻辑的内部不可以去改动内部的变量,其次我们的传入用作比较的Date对象也不可以被改动,所以均是const的.
{
//if (x._day == _day && x._month == _month && x._year == _year)
//{
// return true;
//}
//return false;
return x._day == _day && x._month == _month && x._year == _year;
}
bool Date::operator<(const Date& x) const //同样,这个函数逻辑的内部不可以去改动内部的变量,其次我们的传入用作比较的Date对象也不可以被改动,所以均是const的.
{
if (_year < x._year)
{
return true;
}
else if(_year == x._year && _month < x._month)
{
return true;
}
else if (_year == x._year && _month == x._month && _day < x._day)
{
return true;
}
return false;
}
//3.然后复用已经实现的逻辑,去实现其他的一些运算符重载.
bool Date::operator<=(const Date& x) const
{
//if (*this < x || *this == x) return true;
//return false;
return *this < x || *this == x;
}
bool Date::operator>(const Date& x) const
{
return !(*this <= x);
}
bool Date::operator>=(const Date& x) const
{
return !(*this < x);
}
bool Date::operator!=(const Date& x) const
{
return !(*this == x);
}
//4.然后实现日期类加天数的运算符重载函数
Date& Date::operator+=(int day)
{
if (day < 0) //如果加一个负数,那么相当于减负数的绝对值.
{
return *this -= (-day);
}
_day += day;
while(_day > GetMonthDay(_year, _month))
{
_day -= GetMonthDay(_year, _month);
_month++;
if (_month > 12)
{
_month = 1;
_year++;
}
}
return *this; //为什么要返回它自己? -- 因为+=的逻辑本身就是自己去加.
}
Date Date::operator+(int day) const
{
Date tmp(*this); //这里是用拷贝构造去创建出一个临时的对象,至于为什么我们没有实现还有拷贝构造,因为浅拷贝使用编译器自动生成的就完全够用.
tmp += day;
return tmp; //这里又为什么要重新构造一个临时变量去返回? -- 因为+本来就是创造一个临时变量,像1 + 1,如果我们不用一个变量去接住它,那么答案2我们是拿不到的,
//这种运算符重载的灵活体现,更加加深了我们类的抽象程度,使得其更加贴近内置类型.
}
Date& Date::operator-=(int day)
{
if (day < 0) //同样如果减等于一个负数,相当与加该负数的绝对值.
{
return *this += (-day);
}
_day -= day;
while(_day <= 0)
{
_month--;
//_day += GetMonthDay(_year, _month); //放在这里是错的,因为有可能减到0,减到0,访问的日期就是0.
if (_month == 0)
{
_month = 12;
_year--;
}
_day += GetMonthDay(_year, _month);
}
return *this;
}
Date Date::operator-(int day) const
{
Date tmp(*this);
tmp -= day;
return tmp;
}
//5.然后实现一些自增自减运算符.
Date& Date::operator++()
{
*this += 1;
return *this; // 前置++表达式的返回值,是++之后的值,所以返回的是对象自己(引用).
}
Date Date::operator++(int) // 后置++,这个 int参数只用于编译器区分类型,不用于任何实际操作。
// 在函数内部,你不需要使用这个参数(通常省略其名字以避免未使用参数的警告)。编译器在遇到 obj++时,会自动传递一个 0(或其他任意整数值)给这个虚拟参数。
{
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;
}
//6.然后实现日期类减日期类的天数差
int Date::operator-(const Date& d) const
{
Date max = *this;
Date min = d;
int flag = 1;
if (*this < d)
{
max = d;
min = *this;
flag = -1;
}
int n = 0;
while (max != min)
{
n++;
min++;
}
return n * flag;
}
//7.让我们的类支持<<和>>.
ostream& operator<<(ostream& out, const Date& d)
{
out << "该对象数据:" << "_year=" << d._year << ", _month=" << d._month << ", _day=" << d._day << endl;
return out; //为什么重载之后还需要返回out呢?因为有这种使用场景: cout << d << "你好兄弟"; 就相当于: (cout << d) << "你好兄弟";
}
istream& operator>>(istream& in, Date& d)
{
int year, month, day;
in >> year >> month >> day;
if ((month > 0 && month <= 12) && (day > 0 && day <= d.GetMonthDay(year, month))) //根据不同年份,2月的天数略有差距,所以写一个函数,更好的判断我们的日期天数,当给的数值合法,我们才正常的构造.
{
d._year = year;
d._month = month;
d._day = day;
}
else
{
cout << "非法日期:" << "year=" << year << ", month=" << month << ", day=" << day << endl;
assert(false);
}
return in;
}
一些测试代码:(main.cpp)
cpp
#include"Date.h"
//测试构造函数的逻辑正确性
void test()
{
Date d(1901, 12, 1);
//Date d1(1901, 2, 29);
Date d2(2000, 2, 29);
Date d3(2000, 2, 29);
d.Print();
//d1.Print();
d2.Print();
}
//测试operator==和operator<
void test1()
{
Date d(1901, 12, 1);
//Date d1(1901, 2, 29);
Date d2(2000, 2, 29);
Date d3(2000, 2, 29);
cout << (d2 == d3);
cout << (d2 < d3);
cout << (d < d3);
}
//测试其他运算符重载函数
void test2()
{
Date d(1901, 12, 1);
//Date d1(1901, 2, 29);
Date d2(2000, 2, 29);
Date d3(2000, 2, 29);
cout << (d2 < d3);
cout << (d2 <= d3);
cout << (d > d3);
cout << (d >= d3);
cout << (d != d3);
}
//测试我们日期类加天数的运算符重载函数
void test3()
{
Date d(1901, 12, 1);
d.Print();
Date d1 = d + 100;
d1.Print();
Date d2 = d - 100;
d2.Print();
d.Print();
d += 666;
d.Print();
d -= 666;
d.Print();
//d -= 654; //测试_year的逻辑对不对.
//d.Print();
//d -= 654;
//d.Print();
//d -= 654;
//d.Print();
}
//测试自增自减的功能
void test4()
{
Date d(1901, 12, 1);
d.Print();
Date d1;
d1.Print();
d1 = d++;
d1.Print();
d1 = d--;
d1.Print();
d1 = ++d;
d1.Print();
d1 = --d;
d1.Print();
}
//测试日期类减日期类的天数差的功能.
void test5()
{
Date d(1901, 12, 1);
Date d1(2000, 2, 29);
cout << d1 - d << endl;
cout << d - d1 << endl;
}
//测试我们类适配的cin和cout.
void test6()
{
Date d(1901, 12, 1);
cout << d;
cin >> d;
cout << d;
//该对象数据:_year=1901, _month=12, _day=1
//2000 12 25
//该对象数据:_year = 2000, _month = 12, _day = 25
}
int main()
{
test6();
return 0;
}
全文总结
实践简单类,知行合一有益身体健康。
本文章为作者的笔记和心得记录,顺便进行知识分享,有任何错误请评论指点:)。