我们生活中经常会使用到日期,比如日期加天数,日期-日期,倒计时等等,这里我们要实现的日期类就是来实现这样一些具有实际意义的日期处理~像日期加日期这种是没有实际意义的,我们这里也就不会进行实现~
这里我们创建三个文件~
Date.h 定义日期类结构,对要提供的操作进行声明 Date.cpp 具体实现各种操作 test.cpp 进行各种操作的测试
我们这里实现的日期类是包含年、月、日的,所以我们给到的成员变量就有年、月、日,同时使用private访问限定符进行修饰,不希望只希望在这一个类里面访问成员变量~
#include<iostream>
using namespace std;
//定义日期类
class Date
{
private:
//成员变量 年、月、日
int _year;
int _month;
int _day;//前面加一个下划线便于区分
public:
//成员函数
};//不要忘记末尾的分号
其他文件就可以包含我们自己写的头文件
注意这里是我们自己写的头文件,所以包含头文件的时候应该是双引号" ",而不是尖括号<>
我们不希望使用编译器生成的构造函数让日期是随机值,所以我们这里需要自己写构造函数~
同时在构造的同时,我们也可以检查日期是否合法~
//检查日期是否合法~
bool Date::CheckDate()const
{
if (_month > 12 || _month < 1 || _day <= 0 || _day > GetMonthDay(_year,_month))
{
return false;
}
return true;
}
//Date::Date(int year = 0,int month = 0,int day = 0)//err
Date::Date(int year,int month,int day)//声明和定义不可以同时给缺省参数
//在声明里面给缺省参数
{
_year = year;
_month = month;
_day = day;
//检查日期是否合法
if (!CheckDate())
{
cout << "日期非法!" << *this << endl;
}
}
拷贝构造函数可以用一个已经初始化的对象来初始化新创建的对象,事实上,这里没有涉及到资源管理,我们可以不显示写拷贝构造函数,直接使用编译器自动生成的拷贝构造函数也是没有问题的~
显示写拷贝构造函数
//拷贝构造函数——可以用来初始化新创建的对象
Date::Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
补充:自定义类型传值传参和传值返回会产生临时对象,所以它们都会调用拷贝构造函数~ (C++规定自定义类型对象进行拷贝的行为必须调用拷贝构造)
接下来我们先来一个简单的,也是我们以前练习过的打印日期~
我们给出两种方法,一个是成员函数,一个是流插入运算符重载~
使用成员函数的好处是,每一个成员函数参数都隐含一个this指针,这样我们就可以直接获得当前对象年月日进行打印~
void Date::Print()const
//const 修饰this指针指向的内容,我们不希望内容被修改
{
cout << _year << "年" << _month << "月" << _day << "日" << endl;
}
》<< 流插入运算符有两个操作数,在这里一个是ostream类型,一个是Date类型,如果把它设计成成员函数,那么隐含的this指针在第一个参数,就不符合我们平常使用的习惯~所以我们可以把它设计为全局函数~ 》但是全局函数又不可以访问类的私有成员了,前面我们提到过三种解决方案~~ 1.使用友元函数 2.提供get成员函数,获取年月日 3.将年月日成员变量改为公有
这里我们使用友元函数来达到目的~
在类里面加上friend关键字说明这个函数是这个类的友元函数,可以访问私有成员~
类外面对这个友元函数进行声明,这个时候就不需要再加friend关键字了
在Date.cpp文件里面进行函数的定义~
ostream& operator <<(ostream& ou, const Date& d)
{
ou << d._year << "年" << d._month << "月" << d._day << "日" << endl;
return ou;//有返回值实现连续输出
}
注意:这个重载函数返回值实现我们进行连续输出的操作~同时这里返回值只能使用引用返回~输出流我们是不可以改变的~
知道了流插入运算符重载,流提取运算符重载就十分容易了~
在类里面加上friend关键字说明这个函数是这个类的友元函数,可以访问私有成员~这里与流插入运算符重载不相同的是d不能加const修饰,因为我们本身输入是修改了内容的~
类外面对这个友元函数进行声明,这个时候就不需要再加friend关键字了
在Date.cpp文件里面进行函数的定义~这里还可以增加一个小细节就是检查日期是否合法~
istream& operator>>(istream& in, Date& d)
{
while (1)
{
in >> d._year >> d._month >> d._day;
//检查日期是否合法
if (d.CheckDate())
{
break;
}
else
{
cout << "日期非法,请重新输入!" << endl;
}
}
return in;
}
》对于自定义类型,使用赋值运算符重载可以让我们完成两个已经初始化对象的拷贝~ 》注意与拷贝构造函数进行区分,拷贝构造函数是用一个已经初始化的对象来初始化一个新创建的对象~ 》赋值运算符重载同样有返回值,这样就可以实现连续赋值~
// d1 = d2
//* this d
//这里需要修改*this
Date& Date::operator=(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
return *this;//有返回值,实现连续赋值
}
等于这个很好判断,只需要年月日都是相等的,那么这两个日期就是相等的~
bool Date::operator==(const Date& d)const
{
//年月日相等就相等
return (_year == d._year)
&& (_month == d._month)
&& (_day == d._day);
}
这里有两个方法 》一个是直接写,只要年月日有一个不相同,那么这两个日期就不相等~ 》一个是进行代码复用(更加推荐这个方式,减少代码量)这里日期不相等就是相等的否
bool Date::operator!=(const Date& d)const
{
//1.直接写
/*return (_year != d._year)
|| (_month != _month)
|| (_day != d._day);*/
//2.代码复用
return !(*this == d);
}
判断思路:先判断年,年大就大;年相等判断月,月大就大;月相等就判断日,日大就大
bool Date::operator>(const Date& d)const
{
if (_year > d._year)
{
return true;
}
//年相等判断月
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) || (*this == d);
}
》思路:加的天数先加在日上面得到总天数,判断总天数是否大于当前月份天数,比当前月份天数大就用总天数减去当前月份天数,月加加,再判断是否大于当前月份天数,如此循环~ 》这里我们就需要提供一个获取日期天数的方法,这里我们直接把这个函数定义在类里面,这个函数就成为了内联函数,可以直接在调用的地方进行展开~
获取月份天数:
//获取月份天数
// 直接定义在类里面成为inline函数
int GetMonthDay(int y, int m)const
{
static int arr[13] = { 0,31,28,31,30,31,30,31,31,30,31, 30,31 };
// 0 1 2 3 4 5 6 7 8 9 10 11 12
if (m == 2 && ((y % 400 == 0) || (y % 4 == 0 && y % 100 != 0)))//闰年的二月29天
{
return 29;
}
return arr[m];
}
日期+=天数:
//使用引用返回,减少拷贝次数
Date& Date::operator+=(int day)
{
//特殊处理,day<0,调用日期-=天数
if (day < 0)//调用日期减天数
{
*this -= (-day);
}
else
{
_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);
//特殊处理,day < 0
if (day < 0)
{
tmp -= (-day);
}
else
{
//代码复用
tmp += day;
}
return tmp;
}
》思路:先让日减去当前月天数,如果_day<0,就让_month-- (注意特殊处理_month==0的时候,_year--,_month=1),_day加上_month的天数,再次判断_day是不是小于0
Date& Date::operator-=(int day)
{
if (day < 0)
{
*this += (-day);
}
else
{
_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);
//特殊处理,day < 0,调用+=天数
if (day < 0)
{
tmp += (-day);
}
else
{
//代码复用
tmp -= day;
}
return tmp;
}
》前置++是先++再使用,事实上也就是当前日期本身+=1,我们一样可以使用代码复用~
Date& Date::operator++()
{
*this += 1;
return *this;
}
》这里像前面那样为了与前置++区分,这里参数会有一个int进行区分,没有什么特别的意义,只是为了好区分~ 》后置++是先使用再++,所有我们需要使用一个临时对象来保存++之前的值进行返回~
Date Date::operator++(int)//后面加int与前置++区分
{
Date tmp = *this;
*this += 1;
return tmp;
}
接下来的前置--和后置--有了这些基础,相信就是小菜一碟了~
Date& Date::operator--()
{
*this -= 1;//本身-=1
return *this;
}
Date Date::operator--(int)
{
Date tmp = *this;
*this -= 1;
return tmp;
}
日期-日期是一个比较有意义的计算,而日期+日期没有实际意义,这里就不进行实现了~
》日期-日期,我们可以直接让小日期走到大日期进行计数 》同时注意是前面的日期大,还是后面的日期大~使用一个flag进行标记~
//*this —— d
int Date::operator-(const Date& d)const
{
int flag = 1;//标记,默认前面的日期大
Date max = *this;
Date min = d;
if (*this < d)
{
//后面的日期大,重新赋值
max = d;
min = *this;
flag = -1;
}
int n = 0;//计数
//让小日期走到大日期进行计数
while (min != max)
{
min++;//或者min += 1;
n++;
}
return n * flag;
}
到这里,我们就实现了一个比较完整的日期类~如果小伙伴们有更好的想法~欢迎评论区留言或者私信小编哦❤
#pragma once
#include<iostream>
using namespace std;
//定义日期类
class Date
{
private:
//成员变量 年、月、日
int _year;
int _month;
int _day;//前面加一个下划线便于区分
public:
//成员函数
//构造函数
Date(int year = 0, int month = 0, int day = 0);//全缺省参数
//拷贝构造函数
Date(const Date& d);
//检查日期是否合法
bool CheckDate()const;
//获取月份天数
// 直接定义在类里面成为inline函数
int GetMonthDay(int y, int m)const
{
static int arr[13] = { 0,31,28,31,30,31,30,31,31,30,31, 30,31 };
// 0 1 2 3 4 5 6 7 8 9 10 11 12
if (m == 2 && ((y % 400 == 0) || (y % 4 == 0 && y % 100 != 0)))//闰年的二月29天
{
return 29;
}
return arr[m];
}
//显示日期
//1.成员函数
//后面加const不希望this指针指向的内容被修改
void Print()const;
//2.使用友元,流插入运算符重载,进行输出
//日期d加const不希望内容被修改
friend ostream& operator <<(ostream& ou, const Date& d);
//流提取运算符重载
//日期d不加const,输入本身修改了内容
//输入后需要判断日期是否合法
friend istream& operator>>(istream& in, Date& d);
//日期操作运算符重载
//赋值运算符重载
Date& operator=(const Date& d);
//日期相等==
bool operator==(const Date& d)const;//只是判断,不希望日期被修改 *this 和 d
//不相等!=
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);
//日期-=天数——改变日期本身
Date& operator-=(int day);
//日期-天数,不改变本身
Date operator-(int day);
//前置++——先++再使用,本身发生变化
Date& operator++();
//后置++——先使用再++
Date operator++(int);//后面加int与前置++区分
//前置--
Date& operator--();
//后置--
Date operator--(int);
//日期减日期
int operator-(const Date& d)const;
};//不要忘记末尾的分号
//>>和<<定义为全局函数,符合我们的使用习惯
//Date的友元函数,可以访问Date类里面的私有成员
ostream& operator <<(ostream& ou, const Date& d);//类外不需要再使用friend关键字
istream& operator>>(istream& in, Date& d);
#include"Date.h"//自己写的头文件使用""
//Date::Date(int year = 0,int month = 0,int day = 0)//err
Date::Date(int year,int month,int day)//声明和定义不可以同时给缺省参数
//在声明里面给缺省参数
{
_year = year;
_month = month;
_day = day;
//检查日期是否合法
if (!CheckDate())
{
cout << "日期非法!" << *this << endl;
}
}
//拷贝构造函数——可以用来初始化新创建的对象
Date::Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
//检查日期是否合法~
bool Date::CheckDate()const
{
if (_month > 12 || _month < 1 || _day <= 0 || _day > GetMonthDay(_year,_month))
{
return false;
}
return true;
}
void Date::Print()const
//const 修饰this指针指向的内容,我们不希望内容被修改
{
cout << _year << "年" << _month << "月" << _day << "日" << endl;
}
ostream& operator <<(ostream& ou, const Date& d)
{
ou << d._year << "年" << d._month << "月" << d._day << "日" << endl;
return ou;//有返回值实现连续输出
}
istream& operator>>(istream& in, Date& d)
{
while (1)
{
in >> d._year >> d._month >> d._day;
//检查日期是否合法
if (d.CheckDate())
{
break;
}
else
{
cout << "日期非法,请重新输入!" << endl;
}
}
return in;
}
// d1 = d2
//* this d
//这里需要修改*this
Date& Date::operator=(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
return *this;//有返回值,实现连续赋值
}
bool Date::operator==(const Date& d)const
{
//年月日相等就相等
return (_year == d._year)
&& (_month == d._month)
&& (_day == d._day);
}
bool Date::operator!=(const Date& d)const
{
//1.直接写
/*return (_year != d._year)
|| (_month != _month)
|| (_day != d._day);*/
//2.代码复用
return !(*this == d);
}
bool Date::operator>(const Date& d)const
{
if (_year > d._year)
{
return true;
}
//年相等判断月
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) || (*this == d);
}
//使用引用返回,减少拷贝次数
Date& Date::operator+=(int day)
{
//特殊处理,day<0,调用日期-=天数
if (day < 0)//调用日期减天数
{
*this -= (-day);
}
else
{
_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);
//特殊处理,day < 0
if (day < 0)
{
tmp -= (-day);
}
else
{
//代码复用
tmp += day;
}
return tmp;
}
Date& Date::operator-=(int day)
{
if (day < 0)
{
*this += (-day);
}
else
{
_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);
//特殊处理,day < 0,调用+=天数
if (day < 0)
{
tmp += (-day);
}
else
{
//代码复用
tmp -= day;
}
return tmp;
}
Date& Date::operator++()
{
*this += 1;
return *this;
}
Date Date::operator++(int)//后面加int与前置++区分
{
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;
}
//*this —— d
int Date::operator-(const Date& d)const
{
int flag = 1;//标记,默认前面的日期大
Date max = *this;
Date min = d;
if (*this < d)
{
//后面的日期大,重新赋值
max = d;
min = *this;
flag = -1;
}
int n = 0;//计数
//让小日期走到大日期进行计数
while (min != max)
{
min++;//或者min += 1;
n++;
}
return n * flag;
}