C++运算符的重载--日期类的实现

在学习中,用C++编程的都知道,C++提供了一个非常强大的操作符重载机制,利用操作符重载,我们可以为我们自定义的类增加更多非常有用的功能,本文就是结合自己所学过的知识,对日期类函数进行模拟实现.

目录

1.Date.h

2.Date.cpp

2.1 GetMonthDay()

2.2 IsInvalid()

2.3 运算符重载=

2.4 this指针

2.5 运算符重载==

2.6 运算符重载!=

2.7 运算符重载>

 2.8 运算符重载<

2.9 运算符重载>

2.10 运算符重载>= <=

2.11 运算符重载+

2.12 运算符重载-

2.13 运算符重载+= -=

  2.14 运算符重载++

2.15  运算符重载--

2.16 计算两个日期类之间相差的天数

3.写在最后


1.Date.h

#include
#include
#include

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 };

这样定义的原因是因为:

  1. 月份的下标和实际月份相对应,在取对应的日期的时候更加方便;
  2. 定义成一个数组,更好操作;

但是很明显,闰年是要考虑的一个点,在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操作符(参数列表)

这个功能,我认为在类中十分的重要,可以自己写运算符实现不同类的赋值拷贝等功能.

但是同样也有一些需要注意的点:

  1. 不能通过连接其他符号来创建新的操作符:比如operator@
  2. 重载操作符必须有一个类类型或者枚举类型的操作数
  3. 用于内置类型的操作符,其含义不能改变,例如:内置的整型+,不 能改变其含义
  4. 作为类成员的重载函数时,其形参看起来比操作数数目少个1成员函数,操作符有一个默认的形参this,限定为第一个形参
  5. .* 、:: 、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,很明显这样不对的,这样增加了!=的一个满足条件:

  1. 对==运算符取反
  2. 将&&改成||,应该就可以满足对应的条件

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就自己调试,可以学到很多!!!

你可能感兴趣的:(C++学习,c++)