【C++】类和对象——运算符重载之日期类的实现


请添加图片描述

前言

  前面我们说了类的六个默认成员函数:大家可以自行查看前面的内容:构造和析构函数类和对象的默认成员函数下
  在讲默认成员函数下时我们有提过运算符重载函数,下面我们具体拓展一下各个符号的重载,并用其实现一个日期类。当然,它们使用的规则,可以应用到其他所有类中实现。

注意:运算符重载和函数重载没有直接的关系。
函数重载是让函数名相同,参数不同的函数存在。
运算符重载是让自定义类型可以使用运算符,并且控制运算符的行为,增强可读性
但是多个同一运算符重载可构成函数重载

日期类的实现

要点

  我们想要完成日期类时我们首先要知道它要干什么,它想要实现什么东西,下面我们来一一列举一下:

  1. 日期的大小比较
  2. 计算两个日期间隔的天数
  3. 日期+天数
  4. 日期-天数
  5. 前置++和后置++

日期检查

  当我们在输入日期时,可能会将日期输错,此时我们可以写一个日期检查的函数,用来检查判断我们输入的日期是否正确

bool Date::CheakDay()
{
	if (_year <= 0 || _month <= 0 || _month > 12 || _day<0 || _day>GetMonthDay(_year, _month))
		return false;
	else
		return true;
}

日期的大小比较

  我们将需要的类和所涉及的成员函数及变量一一列出:

class Date
{
public:
	Date(int year, int month, int day)
		:_year(year)
		,_month(month)
		,_day(day)
	{}
	bool operator<(const Date& d1);
	bool operator<=(const Date& d1);
	bool operator>(const Date& d1);
	bool operator>=(const Date& d1);
	bool operator==(const Date& d1);
	bool operator!=(const Date& d1);
private:
	int _year;
	int _month;
	int _day;
};

  下面,我们将这六个比较大小的函数一一实现。
  首先我们来实现小于的运算符重载,我们根据年月日依次比较得出大小:

bool Date::operator<(const Date& d)//加const防止d被改变
{
	if (_year < d._year)
		return true;//年小就小
	else if (_year == d._year)//年相同就比月
	{
		if (_month < d._month)
			return true;
		else if (_month == d._month)//月再相同就比日
			return (_day < d._day);
	}
	return false;
}

我们来测试一下:
在这里插入图片描述
  我们发现,如果我们给d2加了const,我们只能进行d1<d2的比较,而不能反过来写,这是涉及到const权限的问题,d2是const Date类型,而成员函数中隐含的this指针是Date const 类型,所以我们给成员函数加上const关键字进行修饰,使得this指针的类型变为const Date* const类型,使其能进行匹配,为了方便,我们将不用改变日期的函数都用const进行修饰
在这里插入图片描述
  这样可成功实现。
  下面我们再来实现最简单的判断日期是否相等:

bool Date::operator==(const Date& d) const
{
	return _year == d._year
		&& _month == d._month
		&& _day == d._day;
}

  运行测试比较简单,后期大家自行去实现。
  下面继续来实现剩余四种判断,我们已经写了小于和等于的运行规则,剩下四个我们可以直接覆用小于和等于的规则即可,就不用单独再去实现了,如下所示:

bool Date::operator<=(const Date& d) const
{
	return *this < d || *this == d;
}
bool Date::operator>(const Date& d) const
{
	return !(*this <= d);
}
bool Date::operator>=(const Date& d) const
{
	return !(*this < d);
}
bool Date::operator!=(const Date& d) const
{
	return !(*this == d);
}

测试大家自行实现即可。

获取当月的天数

  当我们要对日期进行加减时,要获取当月的天数是必不可少的,那我们就可以写一个函数,将其放到类中,使其变为内联函数,这样我们在使用时就能减少一些调用函数的损耗。
获取当月天数的函数代码如下:

int GetMonthDay(int year, int month)
{
	assert(month > 0 && month < 13);
	static int MonthDayArr[] = { 0,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;
	else
		return MonthDayArr[month];
}

  我们发现,一个简单的获取当前月份天数的函数,其实藏着一些细节,一个就是assert断言,如果月份不在1~12这个范围内就会报错,防止输入错误。第二个就是创建数组的时候我们用了一个static关键字,使这个函数的生命周期延长,将其放在了静态区,不用每调用一次函数就要创建一次数组,这样减少了内存消耗。第三个就是创建数组的第一个元素用0代替,可以让数组下标与月份一一对应,直接将月份作为下标输出即可。

日期+=天数

  我们想要日期+=天数时,我们都会将其直接加到日(_day)上面,然后判断_day的天数是否超过了本月的天数,当超过时,我们就需要用_day减去当前月的天数,然后让月++,注意要判断是否会延伸到下一年,相信大家也都会,我就不再赘述。

Date& Date::operator+=(int n)
{
	_day += n;
	while (_day > GetMonthDay(_year, _month))
	{
		_day -= GetMonthDay(_year, _month);
		++_month;
		if (_month == 13)
		{
			_month = 1;
			++_year;
		}
	}
	return *this;
}

  我们使用了引用返回,减少了消耗,同时使用了前置++,同样也为了减少消耗,这点后面也会讲到。

注意:我们还要区分日期+=天数和日期+天数的区别,一个是会实际改变d的值,而日期+天数中d的值实际上是没有发生变化的,这点需要清楚。懂了这些区别,我们也就可以继续使用覆用来实现日期+天数:

Date Date::operator+(int n)
{
	Date tmp = *this;
	tmp += n;
	return tmp;
}

  这里我们使用值返回,是因为tmp出了函数作用域会被销毁,则不能用引用返回,具体可看上节所说的。我们用了tmp拷贝了*this,用tmp进行+=,这样返回加之后的值,但是 *this没有改变,达到了我们想要的目的。

日期-=天数

  对于日期-=天数和日期+=天数的思路是一样的,都是按照常规去算即可,代码如下:

Date& Date::operator-=(int n)
{
	_day -= n;
	while (_day <= 0)
	{
		_month--;
		if (_month == 0)
		{
			_month = 12;
			_year--;
		}
		_day += GetMonthDay(_year, _month);
	}
	return *this;
}

  同样我们用了引用返回,减少拷贝的消耗。
  日期-天数也是同样的做法:

Date Date::operator-(int n)
{
	Date tmp = *this;
	tmp -= n;
	return tmp;
}

前置++和后置++

  我们要清楚的是前置++和后置++的区别是什么:

  • 前置++:先加后使用,返回+1之后的结果。
  • 后置++:先使用后加,返回+1之前的结果。

  我们发现,如果直接写运算符重载函数,它们两都可以被写成:

Date operator++();

  因此为了区分这两着,我们在后置++的括号里面加了一个int,即:

Date operator++(int);

  虽然说括号里面有个int类型的形参,但是在调用函数时这个参数是不用传递的,编译器会自动传递,只是为了和前置++做出区分。
  明白了这个代码写出来也就很简单了,代码如下:

Date& Date::operator++()//前置++
{
	_day += 1;
	return *this;
}
Date& Date::operator--()//前置--
{
	_day -= 1;
	return *this;
}
Date Date::operator++(int)//后置++
{
	Date tmp = *this;
	*this += 1;
	return tmp;
}
Date Date::operator--(int)//后置--
{
	Date tmp = *this;
	*this -= 1;
	return tmp;
}

  我们可以发现,前置运算符都是用引用返回,而后置运算符都是用值返回,值返回会增加拷贝次数,所以为了减少消耗,提高效率,我们更推荐使用前置运算符。

日期减日期

  在实现日期减日期时,我们首先要知道他们是如何相减得到的。
  按照我们一般思路就是找到两个日期正常相减,但是我们可以直接用一个循环来实现,具体见代码:

int Date::operator-(const Date& d) const
{
	int flag = 1;
	int sum = 0;
	Date max = *this;
	Date min = d;
	if (d > *this)
	{
		max = d;
		min = *this;
		flag = -1;
	}
	while (max != min)
	{
		++sum;
		--max;
	}
	return flag * sum;
}

  这也是一个很巧妙的代码,用了max和min,这样可以保证一直是max–。又多用了一个flag,如果日期1-日期2中,日期1比较小,可以使得得出的结果为负数,一目了然。

完整代码

  有关日期类的实现已经全部完成了,下面是完整代码:

Date.h

#include<assert.h>
#include<iostream>
using namespace std;

class Date
{
public:
	Date(int year, int month, int day)
		:_year(year)
		,_month(month)
		,_day(day)
	{}
	int GetMonthDay(int year, int month)
	{
		assert(month > 0 && month < 13);
		static int MonthDayArr[] = { 0,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;
		else
			return MonthDayArr[month];
	}
	void Print()
	{
		cout << _year << "年" << _month << "月" << _day << "日" << endl;
	}
	void Insert()
	{
		cin >> _year;
		cin >> _month;
		cin >> _day;
		if (!CheakDay())
			cout << "输入错误,请重新输入" << endl;

	}
	bool CheakDay();

	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;
	bool operator!=(const Date& d)const;

	Date& operator+=(int n);
	Date operator+(int n);

	Date& operator-=(int n);
	Date operator-(int n);

	Date& operator++();
	Date& operator--();
	Date operator++(int);
	Date operator--(int);

	int operator-(const Date& d)const;
private:
	int _year;
	int _month;
	int _day;
};

Date.cpp

#include"Date.h"

bool Date::CheakDay()
{
	if (_year <= 0 || _month <= 0 || _month > 12 || _day<0 || _day>GetMonthDay(_year, _month))
		return false;
	else
		return true;
}

bool Date::operator<(const Date& d) const
{
	if (_year < d._year)
		return true;
	else if (_year == d._year)
	{
		if (_month < d._month)
			return true;
		else if (_month == d._month)
			return (_day < d._day);
	}
	return false;
}
bool Date::operator<=(const Date& d) const
{
	return *this < d || *this == d;
}
bool Date::operator>(const Date& d) const
{
	return !(*this <= d);
}
bool Date::operator>=(const Date& d) const
{
	return !(*this < d);
}
bool Date::operator==(const Date& d) const
{
	return _year == d._year
		&& _month == d._month
		&& _day == d._day;
}
bool Date::operator!=(const Date& d) const
{
	return !(*this == d);
}

Date& Date::operator+=(int n)
{
	_day += n;
	while (_day > GetMonthDay(_year, _month))
	{
		_day -= GetMonthDay(_year, _month);
		++_month;
		if (_month == 13)
		{
			_month = 1;
			++_year;
		}
	}
	return *this;
}
Date Date::operator+(int n)
{
	Date tmp = *this;
	tmp += n;
	return tmp;
}

Date& Date::operator-=(int n)
{
	_day -= n;
	while (_day <= 0)
	{
		_month--;
		if (_month == 0)
		{
			_month = 12;
			_year--;
		}
		_day += GetMonthDay(_year, _month);
	}
	return *this;
}
Date Date::operator-(int n)
{
	Date tmp = *this;
	tmp -= n;
	return tmp;
}
Date& Date::operator++()//前置++
{
	_day += 1;
	return *this;
}
Date& Date::operator--()//前置--
{
	_day -= 1;
	return *this;
}
Date Date::operator++(int)//后置++
{
	Date tmp = *this;
	*this += 1;
	return tmp;
}
Date Date::operator--(int)//后置--
{
	Date tmp = *this;
	*this -= 1;
	return tmp;
}

int Date::operator-(const Date& d) const
{
	int flag = 1;
	int sum = 0;
	Date max = *this;
	Date min = d;
	if (d > *this)
	{
		max = d;
		min = *this;
		flag = -1;
	}
	while (max != min)
	{
		++sum;
		--max;
	}
	return flag * sum;
}

test.cpp

这属于调试代码,大家可以自行输入调试。

#include"Date.h"

int main()
{
	Date d1(2024, 7, 24);
	Date d2(2024, 7, 22);
	cout << (d1 < d2) << endl;
	cout << (d2 == d1) << endl;
	cout << (d2 != d1) << endl;
	cout << (d2 > d1) << endl;
	cout << (d2 <= d1) << endl;
	cout << (d2 >= d1) << endl;
	d2 += 70;
	d2.Print();
	Date d3(2030, 7, 1);
	cout << (d3 - d1) << endl;
	return 0;
}

  今天的内容到此结束啦,感谢大家观看,如果大家喜欢,希望大家一键三连支持一下,如有表述不正确,也欢迎大家批评指正。

请添加图片描述

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值