在学习中,用C++编程的都知道,C++提供了一个非常强大的操作符重载机制,利用操作符重载,我们可以为我们自定义的类增加更多非常有用的功能,本文就是结合自己所学过的知识,对日期类函数进行模拟实现.
1.Date.h
#include<stdio.h>
#include<iostream>
#include<assert.h>
using namespace std;
class Date
{
public:
//构造函数
Date(int year = 2008, int month = 1, int day = 1)
:_year(year)
,_month(month)
,_day(day)
{
if (!IsInvalid())
{
assert(false);
}
}
//拷贝构造
//这里拷贝构造采用const修饰,const修饰拷贝构造的值不发生改变
//避免发生一些操作上的失误
Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
//析构函数
~Date()
{
}
//判断输入日期是否符合规定
bool IsInvalid();
//打印函数
void Show();
//赋值运算符重载
Date& 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);
bool operator<(const Date& d);
bool operator>(const Date& d);
//
Date operator+(int day);
Date& operator+=(int day);
//
Date operator-(int day);
Date& operator-=(int day);
//前置++
Date& operator++();
//后置++
Date operator++(int);
//前置--
Date& operator--();
//后置--
Date operator--(int);
//计算两个日期之间的相差天数
int operator-(const Date& d);
private:
int _year;
int _month;
int _day;
};
在头文件的定义中,我们需要注意以下事项,第一,对于一个类来说,其要有构造函数,析构函数,拷贝构造,赋值构造等模块.在书写构造函数的时候,我们对类进行初始化列表进行初始化,这里一定要记住在初始化列表进行初始化的时候,只能进行一次初始化.
2.Date.cpp
#include"Date.h"
//进行日期类函数的实现
//判断是否是闰年
bool IsLeapYear(int year)
{
if ((year % 400 == 0) || (year % 4 == 0 && year % 100 != 0))
return true;
else
return false;
}
//打印日期
void Date::Show()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
//获取对应月的天数
int GetMonthDay(int year, int month)
{
int monthDay[13] = { -1,31,28,31,30,31,30,31,31,30,31,30,31 };
if (month == 2 && IsLeapYear(year))
{
return 29;
}
return monthDay[month];
}
//判断天数是否符合规定
bool Date::IsInvalid()
{
return _year >= 0 && _month >= 0 && _month <= 12
&& _day >= 0 && _day <= GetMonthDay(_year, _month);
}
//赋值运算符重载
Date& Date::operator=(const Date& d)
{
if (this != &d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
return *this;
}
//运算符重载
bool Date::operator==(const Date& d)
{
return this->_year == d._year
&& this->_month == d._month
&& this->_day == d._day;
}
//运算符重载
bool Date::operator!=(const Date& d)
{
return !(*this == d);
}
//运算符重载
bool Date::operator>(const Date& d)
{
if ((this->_year > d._year)
|| (this->_month > d._month && this->_year == d._year)
|| (this->_year == d._year && this->_month == d._month && this->_day > d._day))
{
return true;
}
else
return false;
}
//'<'重载
bool Date::operator<(const Date& d)
{
if ((this->_year < d._year)
|| (this->_month < d._month && this->_year == d._year)
|| (this->_year == d._year && this->_month == d._month && this->_day < d._day))
{
return true;
}
else
return false;
}
//'>='重载
bool Date::operator>=(const Date& d)
{
return (*this > d) || (*this == d);
}
//'<='重载
bool Date::operator<=(const Date& d)
{
return (*this < d) || (*this == d);
}
//'+'加天数重载
Date Date::operator+(int day)
{
Date tmp(*this);
if (day < 0)
{
return *this - (-day);
}
//在临时变量里面加天数
tmp._day += day;
while (tmp._day > GetMonthDay(tmp._year, tmp._month))
{
tmp._day = tmp._day - GetMonthDay(tmp._year, tmp._month);
if (tmp._month == 12)
{
tmp._year++;
tmp._month = 1;
}
else
{
tmp._month++;
}
}
return tmp;
}
//'-'号的重载
Date Date::operator-(int day)
{
Date tmp(*this);
if (day < 0)
{
return *this + (-day);
}
tmp._day -= day;
while (tmp._day < 0)
{
//就要进行借位
if (tmp._day < 0)
{
tmp._month = 12;
tmp._year--;
}
else
{
tmp._month--;
}
int monthday = GetMonthDay(tmp._year, tmp._month);
tmp._day += monthday;
}
return tmp;
}
//'+='的重载
Date& Date::operator+=(int day)
{
*this = *this + day;
return *this;
}
//'-='的重载
Date& Date::operator-=(int day)
{
*this = *this - day;
return *this;
}
//前置++
Date& Date::operator++()
{
*this += 1;
return *this;
}
//后置++
Date Date::operator++(int)
{
Date tmp(*this);
tmp = *this + 1;
return tmp;
}
//前置--
Date& Date::operator--()
{
*this -= 1;
return *this;
}
//后置--
Date Date::operator--(int)
{
Date tmp(*this);
tmp = *this - 1;
return tmp;
}
//计算两个日期之间的相差天数
int Date::operator-(const Date& d)
{
//可以不用减 用相互之间的+进行操作
int flag = 1;
Date max(*this);
Date min(d);
//然后就是再进行判断一下
/*if (max < min)
{
Date tmp = min;
min = max;
max = tmp;
flag = -1;
}*/
if (*this < d)
{
min = *this;
max = d;
flag = -1;
}
//保证好大小之后
int count = 0;
while (min!= max)
{
++min;
++count;
}
return count * flag;
}
void TestDate1()
{
Date d9(2021, 3, 9);
d9.Show();
Date d10(2021, 3, 4);
d10.Show();
int day1 = d9 - d10;
cout << day1 << endl;
}
int main()
{
Date d1;
d1.Show();
Date d2(2022, 2, 24);
d2.Show();
Date d3;
d3 = d2;
d3.Show();
//d3 = d2.operator+(10);
//d3.Show();
//d2.operator+=(20);
//d2.Show();
//Date d4 = d2.operator-(-30);
//d4.Show();
//d2.operator-=(30);
//d2.Show();
//Date d5 = d2++;
//d5.Show();
//++d2;
/*Date d8(2023, 3, 4);
Date d9(2021, 2, 3);
d8.Show();
d9.Show();
int days = d8 - d9;
cout << "相差天数:" << days << endl;*/
TestDate1();
return 0;
}
接下来,梳理一下整个逻辑线,函数所有的命名都采用驼峰法:
2.1 GetMonthDay()
因为不同的年份涉及到的月的天数不同,同样一年中的天数也会不同,这里进行定义一个数组monthDay[13]={ -1,31,28,31,30,31,30,31,31,30,31,30,31 };
这样定义的原因是因为:
- 月份的下标和实际月份相对应,在取对应的日期的时候更加方便;
- 定义成一个数组,更好操作;
但是很明显,闰年是要考虑的一个点,在C语言结构中我们判断过很多次了,同样我们也采用相应的方法进行判断,:
if ((year % 400 == 0) || (year % 4 == 0 && year % 100 != 0))
return true;
else
return false;
2.2 IsInvalid()
判断输入的日期是否符合规定,通过调用上面定义的GetMonthDay进行判断,放在构造函数里面.
2.3 运算符重载=
在C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类 型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似.程序员可以根据自己的需求进行定义相关的类,写出相应的接口函数.
函数名字为:关键字operator后面接需要重载的运算符符号。
函数原型:返回值类型 operator操作符(参数列表)
这个功能,我认为在类中十分的重要,可以自己写运算符实现不同类的赋值拷贝等功能.
但是同样也有一些需要注意的点:
- 不能通过连接其他符号来创建新的操作符:比如operator@
- 重载操作符必须有一个类类型或者枚举类型的操作数
- 用于内置类型的操作符,其含义不能改变,例如:内置的整型+,不 能改变其含义
- 作为类成员的重载函数时,其形参看起来比操作数数目少个1成员函数,操作符有一个默认的形参this,限定为第一个形参
- .* 、:: 、sizeof 、?: 、. 注意以上5个运算符不能重载
Date& Date::operator=(const Date& d)
{
if (this != &d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
return *this;
}
2.4 this指针
this指针是极其特殊的一个指针,那这个this指针存放在哪里呢?学习后发现,其实编译器在生成程序时加入了获取对象首地址的相关代码。并把获取的首地址存放在了寄存器ECX中(VC++编译器是放在ECX中,其它编译器有可能不同)。也就是成员函数的其它参数正常都是存放在栈中。而this指针参数则是存放在寄存器中。类的静态成员函数因为没有this指针这个参数,所以类的静态成员函数也就无法调用类的非静态成员变量.
2.5 运算符重载==
bool Date::operator==(const Date& d)
{
return this->_year == d._year
&& this->_month == d._month
&& this->_day == d._day;
}
2.6 运算符重载!=
通过调用==,就可以实现相应的操作:
bool Date::operator!=(const Date& d)
{
return !(*this == d);
}
不过这里有个bug,当初我刚开始想的时候,不知道怎么冒出一个想法:
bool Date::operator==(const Date& d)
{
return this->_year != d._year
&& this->_month != d._month
&& this->_day != d._day;
}
想的就是我把==运算符的==全部替换成!=就可以,后面在进行其他运算符调用!=的时候才发现到bug,很明显这样不对的,这样增加了!=的一个满足条件:
- 对==运算符取反
- 将&&改成||,应该就可以满足对应的条件
2.7 运算符重载>
bool Date::operator>(const Date& d)
{
if ((this->_year > d._year)
|| (this->_month > d._month && this->_year == d._year)
|| (this->_year == d._year && this->_month == d._month && this->_day > d._day))
{
return true;
}
else
return false;
}
2.8 运算符重载<
bool Date::operator<(const Date& d)
{
if ((this->_year < d._year)
|| (this->_month < d._month && this->_year == d._year)
|| (this->_year == d._year && this->_month == d._month && this->_day < d._day))
{
return true;
}
else
return false;
}
2.9 运算符重载>
bool Date::operator>(const Date& d)
{
if ((this->_year > d._year)
|| (this->_month > d._month && this->_year == d._year)
|| (this->_year == d._year && this->_month == d._month && this->_day > d._day))
{
return true;
}
else
return false;
}
2.10 运算符重载>= <=
//'>='重载
bool Date::operator>=(const Date& d)
{
return (*this > d) || (*this == d);
}
//'<='重载
bool Date::operator<=(const Date& d)
{
return (*this < d) || (*this == d);
}
这个直接调用刚刚写的运算符重载就可以了.
2.11 运算符重载+
Date Date::operator+(int day)
{
Date tmp(*this);
if (day < 0)
{
return *this - (-day);
}
//在临时变量里面加天数
tmp._day += day;
while (tmp._day > GetMonthDay(tmp._year, tmp._month))
{
tmp._day = tmp._day - GetMonthDay(tmp._year, tmp._month);
if (tmp._month == 12)
{
tmp._year++;
tmp._month = 1;
}
else
{
tmp._month++;
}
}
return tmp;
}
这个必须得说道说道,刚开始学的时候觉得这个不是很简单的嘛,完全没有难度,等到自己写的时候,大脑一片空白,要想学好编程,必须多学多练,将自己的想法写到程序上去,能正常运行才算是学会这种方法.
很明显,我们可以调用我们刚刚写的运算符重载函数,对应Date日期类加天数,月份不断地自加,要减去对应自己月份的日期,直到最后的天数小于对应的月份的天数,返回一个类.但是要记住:这里只是+,并不是+=返回的是一个类的临时拷贝,并没有改变对应的this指针指向的类.
2.12 运算符重载-
Date Date::operator-(int day)
{
Date tmp(*this);
if (day < 0)
{
return *this + (-day);
}
tmp._day -= day;
while (tmp._day < 0)
{
//就要进行借位
if (tmp._day < 0)
{
tmp._month = 12;
tmp._year--;
}
else
{
tmp._month--;
}
int monthday = GetMonthDay(tmp._year, tmp._month);
tmp._day += monthday;
}
return tmp;
}
2.13 运算符重载+= -=
//'+='的重载
Date& Date::operator+=(int day)
{
*this = *this + day;
return *this;
}
//'-='的重载
Date& Date::operator-=(int day)
{
*this = *this - day;
return *this;
}
2.14 运算符重载++
//前置++
Date& Date::operator++()
{
*this += 1;
return *this;
}
//后置++
Date Date::operator++(int)
{
Date tmp(*this);
tmp = *this + 1;
return tmp;
}
这个前置++,必须好好说道说道,前置和后置的区别在于:开没开临时变量,有没有将值传给this指针,在C语言中我们也学习到了前置++和后置++的区别,在这里总结一下是指:前置++改变了this指针指向的类本身,而后置++我们则是新开了一个tmp的临时Date类变量,return这个临时的类,*this发生改变.
2.15 运算符重载--
//前置--
Date& Date::operator--()
{
*this -= 1;
return *this;
}
//后置--
Date Date::operator--(int)
{
Date tmp(*this);
tmp = *this - 1;
return tmp;
}
2.16 计算两个日期类之间相差的天数
//计算两个日期之间的相差天数
int Date::operator-(const Date& d)
{
//可以不用减 用相互之间的+进行操作
int flag = 1;
Date max(*this);
Date min(d);
//然后就是再进行判断一下
/*if (max < min)
{
Date tmp = min;
min = max;
max = tmp;
flag = -1;
}*/
if (*this < d)
{
min = *this;
max = d;
flag = -1;
}
//保证好大小之后
int count = 0;
while (min!= max)
{
++min;
++count;
}
return count * flag;
}
这个代码刚开始出现一点bug,其实本质上不难,但是因为我的运算符重载!=写错了,我写成!=运算符重载的第二种方式,在进入while循环中发现:当this指向的日期类有一项相等时,!=就为false,整体就返回false,就跳出while循环了,所以那种方法不正确,要注意.
3.写在最后
在日常练习的时候,一定不要偷懒,要认认真真地将这些类自己重现一遍,出现bug就自己调试,可以学到很多!!!
版权归原作者 胡须不排序H 所有, 如有侵权,请联系我们删除。