[C++] 类与对象(中) 一篇带你解决运算符重载实例--日期类Date

上篇文章我们了解了运算符重载,本篇文章我们将通过一个实例:日期类来巩固运算符重载的知识。

目录

1.全缺省的构造函数

2. operator ==

3.operator <

4.operator <=

5.operator !=

6.operator >

7.operator >= 

8.operator+=(int day) 日期+=天数

9.operator+(int day) 日期+天数

10.operator-=(int day) 日期-=天数

11.operator-(int day) 日期-天数

12.前置++,前置--,后置++,后置--

13.日期-日期

完整代码:


本篇文章我们将主要实现一下接口。

class Date
{
public:
	// 获取某年某月的天数
	int GetMonthDay(int year, int month);
	// 全缺省的构造函数
	Date(int year = 1900, int month = 1, int day = 1);
	// 拷贝构造函数
    //d2(d1) 
	Date(const Date& d);
	// 赋值运算符重载
    // d2 = d3 -> d2.operator=(&d2, d3)
	Date& operator=(const Date& d);
	// 析构函数
	~Date();
	// 日期+=天数
	Date& operator+=(int day);
	// 日期+天数
	Date operator+(int day);
	// 日期-天数
	Date operator-(int day);
	// 日期-=天数
	Date& operator-=(int day);
	// 前置++
	Date& operator++();
	// 后置++
	Date operator++(int);
	// 后置--
	Date operator--(int);
	// 前置--
	Date& operator--();
	// >运算符重载
	bool operator>(const Date& d);
	// ==运算符重载
	bool operator==(const Date& d);
	// >=运算符重载
	inline bool operator >= (const Date& d);
	// <运算符重载
	bool operator < (const Date& d);
	// <=运算符重载
	bool operator <= (const Date& d);
	// !=运算符重载
	bool operator != (const Date& d);
	// 日期-日期 返回天数
	int operator-(const Date& d);

private:
	int _year;
	int _month;
	int _day;

};

 此项目为正式的日期类项目,因为我们可以多文件存储。主要包含以下文件:

1.全缺省的构造函数

构造函数类似于初始化,其中需要注意的是要对日期的合法性进行判断。由于每个月份的天数存在差别,这里在day中我们需要调用一个GetMonthDay函数接口。

int Date::GetMonthDay(int year, int month)
{
	assert(year >= 0 && month > 0 && month < 13);
	//静态区开一次
	static int monthDayArray[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
	if (month == 2 && isLeapYear(year))
	{
		return 29;
	}
	return monthDayArray[month];
}

由于每年有12个月份,每个月的天数存在微小差别,因此我们选择用数组存放对应月份的天数。这里我们月份对应的下标放对应月份的天数,方面调用。由于我们会多次调用此函数,并且我们不会对其中的内容进行更改。因此我们可以将开辟的数组放在静态区。只需要一次初始化可以实现多次调用的功能。提高的程序的效率。 

由于闰年和平年的2月份天数存在差别,因此我们要对2月份这种特殊情况进行判断,如果是闰年就直接返回29,如果是平年就正常返回。

	//判断是否闰年
	bool isLeapYear(int year)
	{
		return (year % 100 != 0 && year % 4 == 0) || (year % 400 == 0);
	}

Date::Date(int year, int month, int day)
{
	if (year >= 1 && month <= 12 && month >= 1 && day >= 1 && day <= GetMonthDay(year, month))
	{
		_year = year;
		_month = month;
		_day = day;
	}
	else
	{
		cout << "您输入的日期不合法" << endl;
		exit(-1);
	}
}

2. operator ==

要判断两个日期是否相等,需要判断两个日期的年月日分别相等。因此我们需要对三个结果进行逻辑与判断,最终返回布尔结果。

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

3.operator <

要判断日期<日期。

1、首先要比较对应的年,如果年小后面无需比较,直接返回true.

2、如果年相同,再比较月,如果月小无需比较天,直接返回true.

3、如果年月都相同,此时比较天,如果天小返回true.

因此以上3种情况满足任意一种就返回true,否则返回false.

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

4.operator <=

由于我们已经书写了<和==,因此如果我们要判断<=我们只需要复用上述写好的两个接口即可。逻辑为<和==都满足<=。因此<和==满足其中一个即可返回true,否则返回false.

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

5.operator !=

!=和==构成逻辑非,两个接口逻辑相反,因此只需要给==取反即可。

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

6.operator >

>和<=构成逻辑非,因此只需要对<=取反即可。

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

7.operator >= 

>=和<构成逻辑非,因此只需要对<取反即可

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

我们发现,我们只需要实现==和<(或>)两个接口即可实现所有的比较接口,由于复用的接口代码量偏少,使用频繁。因此我们可以将后面复用其他接口的接口置成内敛(inline)函数。因此我们可以写在类中。因为类中默认成员函数为内敛函数。

8.operator+=(int day) 日期+=天数

根据逻辑可知,一个日期加上一个天数,将返回一个新的日期。因此这里函数的返回值应为一个日期类Date。

为了实现这个接口,我们可以举例来进行分析:

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

测试结果:2022-8-27  经过验证正确。

需要注意的是:如果传入的day小于0,我们先对day取相反数再处理即可。 

9.operator+(int day) 日期+天数

由于我们已经实现了日期+=天数,因此我们可以复用。+=和+唯一的区别是+=改变本身,+不改变。因此我们创建一个新的Date,存放结果即可。

Date Date:: operator+(int day)
{
	Date ret(*this);
	ret += day;

	return ret;
}

测试结果:此时我们发现d1并没有改变,d2改变了。结果正确。

10.operator-=(int day) 日期-=天数

逻辑同+=类似,只需要这里注意一些细节节即可,我们同样可以使用一个例子分析

这里如果要是用--,一定是前置--,因此自身是改之后再使用。

需要注意的是:如果传入的day小于0,我们先对day取相反数再处理即可。 

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

我们同样使用8月27减去100如果返回5月19说明正确。

测试结果正确

11.operator-(int day) 日期-天数

 道理同+=和+的关系相同,同样采用复用。

Date Date::operator-(int day)
{
	Date ret(*this);
	ret -= day;

	return ret;
}

测试:

12.前置++,前置--,后置++,后置--

我们发现前置++和后置++的操作符都是++操作符,并且他们的返回类型,函数名,参数列表都一模一样。那么他们我们应该怎么区别呢?

这个问题其实编译器也考虑到了,因此编译器为了区别前置++和后置++,编译器规定,后置++(或后置--)的参数列表中多穿一个值。此时因为两个函数的参数列表的参数个数不同,两个函数就构成了函数重载。就可以区分两个操作符了。

需要注意的是,加的这个参数是为了区分前置和后置,因此传过来的值无所谓。我们一般可以省略写就直接只写数据类型。

因此:前置++就相当于是+=1,

	Date& operator++()
	{
		*this += 1;
		return *this;
	}

前置--就相当于-=1

	Date& operator--()
	{
		*this -= 1;
		return *this;
	}

后置++先使用,因此要创建一个tmp存上,再自身++

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

后置--先使用,因此要创建一个tmp存上,再自身--

	Date operator--(int)//加参数为了区分,与传过来的值无关
	{
		Date tmp(*this);
		*this -= 1;
		return tmp;
	}

测试:结果正确

注意:由于前置++,前置--,后置++,后置--代码使用次数多,代码篇幅小,因此我们可以写在类内成为内联函数。

13.日期-日期

这个接口我们要实现一个日期与另一个日期之间相差多少天。如果是小日期-大日期,应该返回一个负数。因此我们要对这个小细节进行处理。

这里的实现逻辑是:

1、我们先默认第一个日期为大日期,第二个日期为小日期。

2、我们在对这两个进行比较,如果第一个是小日期,我们就将max和min进行日期调换。如果调换说明是小日期-大日期,最终的结果理应该是个负数。因此我们定义一个flag = 1,如果这里调换的话,让flag = -,最终返回n*flag即可。

3、我们要始终保持小追大。我们使用前置++运算,让小的日期追大的日期,这里需要定义一个计数器n。每++一次,n就++一次。

int Date::operator-(const Date& d)
{
	int flag = 1;
	Date max = *this;//默认认为第一大 
	Date min = d;
	if (*this < d)
	{
		min = *this;
		max = d;
		flag = -1;
	}
	int n = 0;
	while (min != max)
	{
		++n;
		++min;
	}
	return n*flag;
}

测试:结果正确

完整代码:

类与对象(2) 日期类完整代码

我已经实现完啦,各位小伙伴可以自测一下哦~ 

(本篇完)

猜你喜欢

转载自blog.csdn.net/qq_58325487/article/details/124866653