前文:
小编在前文讲述了类和对象的一部分内容,其中小编讲述过运算符重载这个概念以及一个时间类,当时小编讲的没有那么细致,下面小编将会讲述时间类来帮助各位读者朋友更好的去理解运算符重载,那么,代码时刻到~
目录
[1.6..时间 +/+= 天数函数](#1.6..时间 +/+= 天数函数)
[1.7.时间 -/-= 天数函数](#1.7.时间 -/-= 天数函数)
正文:
1.时间类
在讲解时间类的诸多功能之前,各位读者朋友需要先去了解一下时间类的成员变量有什么,这个小编在之前写构造函数的时候讲到过,不过此时肯定还有读者朋友是第一次来看我的文章,所以小编重新说一下,时间类的成员变量其实是很简单的,通过名字我们就知道和时间有关,所以它的成员变量应该是:年(_year),月(_month),日(_day),它的代码如下图所示,现在小编已经讲述了成员变量如何去写了,下面我们开始功能的书写~
cpp
class Date
{
private:
int _year;
int _month;
int _day;
};
1.1.时间类的构造函数
我们在写一个完整的类的时候,自然需要写这个类的构造函数,构造函数小编在之前的博客也讲过,简单来说就是让成员变量进行初始化的,当然小编那时候讲的构造函数还不是完全体构造函数,构造函数还有一个点小编没讲,对于这部分内容小编会在类和对象(3)这篇文章讲过,敬请期待,行了废话不多说,由于构造函数的难度系数没有这么大,小编就直接展示代码了:
cpp
Date(int year = 2024, int month = 9, int day = 20)//小编此时是带缺省值的构造函数,此时小编就以文章创作的日期作为缺省值了
{
_year = year;
_month = month;
_day = day;
}
1.2.时间类的拷贝构造函数
我们写完了构造函数以后,自然也会有拷贝构造函数的书写,其实对于时间类这个类来说,拷贝构造函数是无须书写的,因为时间类的成员变量并没有涉及到资源的调用,都是内置类型,此时我们直接使用系统默认给我们生成的拷贝构造函数就好,内置类型我们仅需使用浅拷贝即可,对于一些资源的调用例如动态内存开辟的类型我们就必须写拷贝构造函数,不过此时时间类是为了让各位更好的去掌握之前学习的内容,所以小编就把之前讲的都写一遍了,此时拷贝构造函数也是比较容易的,我们仅需把形参中的x类类型对象的成员变量给予刚建立的对象即可,下面小编直接展示代码:
cpp
Date(const Date& x)//const是为了防止权力放大的问题
{
_year = x._year;
_month = x._month;
_day = x._day;
}
1.3.时间类的析构函数
时间类的析构函数其实也无需去写的,因为我们没有牵扯到资源的开辟,所以我们无须自己去释放资源,内置类型系统是会帮助我们去进行销毁的,但是为了帮助各位读者朋友去理解析构函数,小编就简单写一个析构函数,析构函数的写法也是很容易,小编的方法就是直接把成员变量的值改为0即可,由于难度不大,小编直接展示代码:
cpp
~Date()
{
_year = _month = _day = 0;
}
1.4.时间类的赋值运算符重载
赋值运算符重载是小编在上篇文章刚刚讲述的内容,对于赋值运算符来说,它其实和拷贝构造函数是类似的,如果一个类的成员变量是不需要开辟资源的话,那么完全可以不用去写,内置类型我们仅需浅拷贝就满足情况了,不过为了让各位读者朋友更好的去掌握赋值运算符重载,小编还是会去写(感觉这句话写的太多就显得我水字数了,等会各种功能的讲解小编就不叨叨这么多了),赋值运算符重载函数其实和拷贝构造长的是很像的,我们传过去的参数也是一个类类型的对象x,我们仅需把x的内容复制给要接受的对象,对于拷贝构造函数和赋值运算符重载小编在上篇文章也讲述了,感兴趣的读者朋友可以去看看,下面小编展示代码:
cpp
Date& operator=(const Date& x)
{
_year = x._year;
_month = x._month;
_day = x._day;
return *this; //记住一定要有返回值,有个人每次都忘记
}
1.5.时间类的比较大小函数
在写这个比较大小的函数之前,小编先提前透露一下难度,当我们写完前两个运算符重载以后,剩下的会很简单,可能读者朋友会好奇,好奇就对了,下面跟着小编的脚步开启比较大小函数的书写~
1.5.1.等于运算符重载
首先我们重载的就是等于运算符,等于运算符的重载是在这一系列比较大小的函数中最为容易的,对于两个类的相等,如果两个;类的成员变量相等,那么这两个类就是相等的,由于难度不大,下面小编给各位读者朋友展示一下代码:
cpp
bool operator==(const Date& x)
{
return _year == x._year && _month == x._month && _day == x._day; // 只有三个都成立了那么这个返回值就是真的,反之则就是假的
}
1.5.2.大于运算符重载
大于运算符重载算是一个比较麻烦的运算符重载了,对于两个类的时间比大小,原理还是比较容易的,我们就拿这两个类类型的对象为A,B,如果A的年大于B的年,那么此时就说明A大于B,返回真;如果A的年和B的年相等,不过A的月大于B的月,那么就说明A大于B,返回真;如果A的年月和B的年月相等,不过此时A的日大于B的日,那么此时A大于B,返回真;以上情况都说明了A大于B,如果这几种情况都不符合的话,那么直接返回假即可,这便是大于运算符的重载,理解起来是很容易的,但是写起来是不容易的,我们得用多个if else语句,下面小编给出代码:
cpp
bool operator > (const Date& x)
{
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;
}
1.5.3.大于等于运算符重载
可能很多读者朋友在看完前面小编讲述的赋值运算符重载以后,可能现在很会快就想到解决方案,我们仅需复制上面的代码,然后把>全部改成>=就可以完成大于等于的操作,这个操作确实是正确的,不过也很复杂代码写起来,还记得小编在开头说的吗,我们仅需学完前两个函数,后面的函数会非常简单,下面小编就解释原因,对于后面的函数,我们仅需复用前面两个函数就可以解决所有的比较问题,就拿此时的大于等于举例子,大于等于的意思无非就是大于或者等于成立,各位读者朋友仔细想想,大于函数我们实现了没》等于函数我们实现了没?全都实现了,所以我们仅需通过或运算符去链接它们,即可完成大于等于运算符的重载,如下面的代码所示:
cpp
bool operator>=(const Date& x)
{
return *this > x || *this == x;
}
1.5.4.不等于运算符重载
想一想不等于的反义词是什么,那肯定是等于,等于运算符我们也写出来了,所以我们仅需让等于运算符取!,就可以实现不等于运算符重载,代码如下所示:
cpp
bool operator != (const Date& x)
{
return !(*this == x);
}
1.5.5.小于运算符重载
小于运算符重载的实现和上面不等于重载的原理是一样的,小于的相反是什么?那肯定是大于等于,大于等于的运算符我们重载了没?答:指定是重载了,所以我们仅需让大于等于运算符取反即可,是不是感觉实现完了前面两个函数,之后的函数有一种轻舟已过万重山的滋味?这便是代码的有趣之处,下面小编给出代码:
cpp
bool operator < (const Date& x)
{
return !(*this >= x);
}
1.5.6.小于等于运算符重载
这个小于等于的重载和上面几位仁兄的原理是一样的,此时我们仅需找到它的反,它的反是大于,我们仅需让大于操作符取反即可,下面展示代码:
cpp
bool operator <= (const Date& x)
{
return !(*this > x);
}
此时我们已经完成了比较功能的实现,是不是很轻松,下面我们继续往前走,开始更多功能的学习!
1.6..时间 +/+= 天数函数
1.6.1.获取天数的函数
在我们讲述时间+/+=天数函数之前,我们首先要先写一个确定每个月天数的函数,因为大家都知道,闰年和平年2月份的天数不一样,每个月的天数也不是一致的,所以此时我们需要自己写一个获取每个月天数的函数供我们使用,对于这个函数的描写其实很简单,我们可以先设置一个可以保存13个整形的数组,然后由于数组第一个元素的下标是0开头的,所以我们第一个元素不作数,其他的就是按照1到12月天数来算的,此时2月份我们设置29天,之后我们再写一个判断平年闰年的函数即可(这一个是我们在C语言阶段常常写的,小编就不详细介绍了),如果此时是2月并且是闰年,那么直接返回29;如果不是的话,直接返回对应下标的数组即可,下面小编给出函数:
cpp
inline int GetMonthday(int year,int month)
{
static int arr[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;
}
return arr[month];
}
1.6.2.+=天数函数
下面我们就可以进行+=天数运算符的重载了,我们知道,就拿整型举例,1+=2 就是 1 + 2 ==3 ,此时直接让1加上2了,对于此时类+=天数,我们也是让类本身进行了改变,如果是1+2的话,是把1+2的结果给另一个整型,整型本身不发生变化,同样的,类+天数,类本身不发生变化这个要和+区别清楚,下面我们来讲述一下+=天数的原理:
此时小编的做法就是直接让对象的天数加上传过来的天数, 然后我们就要开始往后进位了,我们首先先让天数减当月的天数,然后让天数往后加一,之后我们需要判断此时的月份是否加到了13,如果是13,那么就让月份变为1,年往后+1,之后我们继续去减去当月的天数,此时我们就用到了循环,循环的条件自然是此时的天数应当大于当月的天数,在我们循环完以后,返回*this即可~此时我们的返回值类型是引用类类型,这样做是为了减少临时对象的产生,提高代码的效率,这个函数已经写完,下面小编给出这个函数的代码:
cpp
Date& operator+=(int day)
{
_day += day;
while (_day > GetMonthday(_year, _month))
{
_day -= GetMonthday(_year, _month);
_month++;
if (_month == 13)
{
_month = 1;
_year++;
}
}
return *this;
}
1.6.3.+天数函数
当我们写完上面那个运算符以后,这个函数其实就变的很简单了,我们还是复用了上面那个函数,首先我们需要在创建一个类对象,把*this拷贝赋值给它,之后我们让这个新建立的对象+=天数以后,返回这个新建对象即可,此时我们返回值类型是不能为引用类类型了,而是单纯的类类型,因为此时我们返回的内容是一块临时对象,此时这个新建立的类对象函数栈帧结束以后就销毁了,我们如果使用引用会造成野引用的问题,下面小编就展示+函数的写法:
cpp
Date operator+(int day)
{
Date a = *this;
a += day;
return a;
}
1.7.时间 -/-= 天数函数
1.7.1.-=天数函数
+=函数写完了,此时我们自然也有-=天数函数,下面废话不多说,小编直接讲述这个函数的写法:首先,我们直接让成员变量中的天数减去给我们的天数,之后我们就要采取借位的方法,首先我们先让------month减1,先判断此时的月份是否等于0,如果等于0的话,我们就让月份等于12,年份往后倒退一年,此时让天数+=当月的天数,然后我们依然采取循环的方式,此时的循环条件就是_day <= 0,之后循环结束以后,我们返回*this即可,此时我们的-=运算符就算是重载结束,下面小编给出代码:
cpp
Date& operator-=(int day)
{
_day -= day;
while (_day <= 0)
{
_month--;
if (_month == 0)
{
_month = 12;
_year--;
}
_day += GetMonthday(_year, _month);
}
return *this;
}
1.7.2.-天数函数
此时这个函数的写法和上面的+写法如出一辙,只不过我们就把上面函数的+改成了-即可,小编就不多重复无用的话了,直接上代码:
cpp
Date operator-(int day)
{
Date a = *this;
a -= day;
return a;
}
1.8.++/--函数
在讲解++ / --运算符之前,小编先给各位读者朋友科普一个小小的知识点,关于如果区分前置++和后置++的问题,一个图片便可以让读者朋友们去了解到,如下图所示:
1.8.1.前置++运算符重载
对于前置++运算符,我们知道它的规则就是先+1后使用,所以我们可以先让它的本身+1 ,然后直接返回*this即可,此时就用到了我们上面刚刚实现过的+=天数函数了,从这我们又看出,我们前面都在为后面的学习铺路,下面小编展示代码:
cpp
Date& operator ++()
{
*this += 1;
return *this;
}
1.8.2.后置++运算符
后置++的运算法则是先使用后++,所以我们需要先设置一个对象来负责接受*this的值,然后我们让*this正常+1,之后返回刚刚设置的对象即可,并且它需要在括号内加个int和前置++区分,下面小编给出代码:
cpp
Date operator++(int)
{
Date a = *this;
*this += 1;
return a;
}
1.8.3.前置--运算符
对于前置--运算符,我们知道它的规则就是先-1后使用,所以我们可以先让它的本身-1 ,然后直接返回*this即可,此时就用到了我们上面刚刚实现过的-=天数函数了,从这我们又看出,我们前面都在为后面的学习铺路,下面小编展示代码:
cpp
Date& operator--()
{
*this -= 1;
return *this;
}
1.8.4.后置--运算符
后置++的运算法则是先使用后--,所以我们需要先设置一个对象来负责接受*this的值,然后我们让*this正常-1,之后返回刚刚设置的对象即可,并且它需要在括号内加个int和前置--区分,下面小编给出代码:
cpp
Date operator--(int)
{
Date x = *this;
*this -= 1;
return x;
}
1.9.时间减去时间
时间可以进行想减,但是不可以去相加,因为加起来是完全没意义的,但是减是有意义的,因为这常常用于计算两个日期之间的天数,就比如今天距离国庆还有多少天,此时小编写博客时间距离国庆还有10天,小编还是很期待国庆的,可以更好的去学习了(确信),行了不多说废话了,下面小编就要讲述这个的原理了:
此时我们需要先判断这两个时间的长短,所以先设置好两个对象,分别为a1,a2,首先我们先把长的时间给a1,短的给a2,对于判别长短的方法,小编在数据结构的快慢指针的时候就用到过,我们先默认设置一个对象就是长的,之后在做判断,此时我们还需要flag变量来控制正负,如果*this是大于给定的x的,那么此时就是正的,反之为负,并且我们还需要设置一个变量count,这个是来计数的,我们通过循环的方式,让短的不断++,直到++到长的,此时循环结束,我们也计算出了天数count,之后我们返回count*flag即可,这个方法小编认为还是比较容易理解的,同样的,我们也是用到了前面我们使用过的函数,下面小编给出代码:
cpp
int operator-(Date& s1)
{
Date a1 = *this;
Date a2 = s1;
int flag = 1;
if (a1 < a2)
{
a1 = s1;
a2 = *this;
flag = -1;
}
int count = 0;
while (a2 != a1)
{
a2++;
count++;
}
return count * flag;
}
此时我们实现完了时间类,当然这个时间类是不太完整的,小编还有流插入和流提取函数还没有写完,不过小编不打算讲解这个了,等以后深入学习了cin和cout以后小编在回来补全这篇文章,免得写一个错的让各位读者朋友理解错了就不好了,下面小编展示完整的代码~
2.代码展示
2.1.Date.h
cpp
#include<iostream>
using namespace std;
//现在开始来实现日期类的完整实现
class date
{
public:
friend ostream& operator<<(ostream& out, date& s1); //因为要用到类里面的,所以我们要用友元函数,如果不这么做,会显得很奇怪
friend istream& operator>>(istream& in, date& s1);
//先来构造函数
date(int year = 2024, int month = 8, int day = 12);
//拷贝构造函数
date(const date& s1);
//析构函数
~date();
//赋值运算符重载
date& operator=(const date& s1);
bool operator<(const date& s1);
bool operator<=(const date& s1);
bool operator>(const date& s1);
bool operator>=(const date& s1);
bool operator==(const date& s1);
bool operator!=(const date& s1);
// 获取某年某月的天数
int GetMonthDay(int year, int month)
{
static int arr[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;
}
return arr[month];
}
//写一个日期加天数(+=的实现,因为这样变化会导致本身发生变化)
date& operator+=(int day);
//实现+操作,此时不会导致本身变化
date operator+(int day);
//加实现完了就该实现减等了
date& operator-=(int day);
//实现完了减等就该实现减操作符了,此时本身不改变,不过一定记得此时返回的是一个里面创建的临时变量,不可以用&引用返沪了
date operator-(int day);
//通过打印来看看自己写的函数对不对
//前置++
date& operator++();
//后置++,为了区分,里面得放一个int,这样才可以分辨出来
date operator++(int);
//前置--
date& operator--();
//后置--
date operator--(int);
//日期减日期
int operator-(date& s1);
void Print()
{
cout << _year << "/" << _month << "/" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
//还有个流插入操作符和流提取操作符
ostream& operator<<(ostream& out,date& s1);
istream& operator>>(istream& in, date& s1);
2.2.Date.cpp
cpp
#include"date.h"
date :: date(int year, int month , int day) //首先,缺省值只可以在函数声明中体现,其次想在这个文件使用定义就必须用::因为如果不用编译器不会去类域找
{
_year = year;
_month = month;
_day = day;
}
bool date :: operator<(const date& s1)
{
if (_year < s1._year) //先比年,如果年就可以比出来直接结束
{
return true;
}
else if (_year == s1._year && _month < s1._month) //年如果相等直接比月,如果月可以比较出来直接结束
{
return true;
}
else if (_year == s1._year && _month == s1._month && _day < s1._day) //年月都相等,那就日
{
return true;
}
return false; //上面的情况都不符合那就只有你了
}
bool date :: operator==(const date& s1)
{
if (_year == s1._year && _month == s1._month && _day == s1._day)
return true;
return false;
}
bool date :: operator<=(const date& s1)
{
return (*this < s1 || *this == s1);
}
bool date :: operator>(const date& s1)
{
return !(*this <= s1); //运算符进行重复利用我们就可以实现剩余的比较,而不是成为一个ctrl cv大师
}
bool date :: operator>=(const date& s1)
{
return !(*this < s1);
}
bool date :: operator!=(const date& s1)
{
return !(*this == s1);
}
date::date(const date& s1)
{
_year = s1._year;
_month = s1._month;
_day = s1._day;
}
date :: ~date()
{
_year = 0;
_month = 0;
_day = 0;
}
date& date:: operator=(const date& s1)
{
_year = s1._year;
_month = s1._month;
_day = s1._day;
return *this;
}
date& date:: operator+=(int day)
{
_day += day;
if (_day < 0)
{
*this -= -_day;
}
else
{
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 += day;
return tmp;
}
date& date :: operator-=(int day)
{
_day -= day;
if (_day > 0)
{
*this += -_day;
}
else
{
while (_day <= 0)
{
--_month;
if (_month == 0)
{
_month = 12;
--_year;
}
_day += GetMonthDay(_year, _month);
}
}
return *this;
}
date date:: operator-(int day)
{
date tmp = *this;
tmp -= day;
return tmp;
}
date& date:: operator++()
{
*this += 1;
return *this; //前置++是先++后使用,所以直接返回加完后的就可以
}
date date:: operator++(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;
}
int date:: operator-(date& s1)
{
date a1 = *this;
date a2 = s1;
int flag = 1;
if (a1 < a2)
{
a1 = s1;
a2 = *this;
flag = -1;
}
int count = 0;
while (a2 != a1)
{
a2++;
count++;
}
return count * flag;
}
ostream& operator<<(ostream& out, date& s1)
{
out << s1._year << "年" << s1._month << "月" << s1._day << "日" << endl;
return out;
}
istream& operator>>(istream& in, date& s1)
{
cout << "请输入:";
in >> s1._year >> s1._month >> s1._day;
return in;
}
3.总结
这篇文章算是写完了,正如小编在文章所说,时间类就像冒泡排序一样,它们都是具有教学意义的,时间类帮我们去更好的巩固类的一些功能,小编写这篇文章写得很快,因为时间类我实现了好几次,因为写的快,所以文章难免会有一些错误,如果文章有错误的话,可以在评论区指出,小编一定会及时的回复信息,那么,我们下一篇博客见啦!