[C++](5)类和对象练习,日期类的实现,运算符重载

日期类的实现

日期类的实现主要是练习运算符重载,只要是对于日期的计算有意义的运算符都应该重载。

注:本篇文章的日期只考虑公元后的。

函数实现

在中篇我们已经实现了==<的重载:

重载==

比较两个日期是否相同

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

重载<

判断左值日期是否在右值日期的前面

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

构造函数

构造函数应该对用户传进来的日期初始化进行检查

为此,我们首先要判断年月日是否在合法的范围,要判断day的范围,需要GetMonthDay函数获得这个月的天数,同时还要用isLeapYear判断是否是闰年,因为isLeapYear函数较短,所以可以直接写在类里面作为内联。

//.h
bool isLeapYear(int year)
{
    return year % 4 == 0 && year % 100 != 0 || year % 400 == 0;
}
//.cpp
int Date::GetMonthDay(int year, int month)
{
	assert(year >= 1 && month >= 1 && month <= 12);
	static int monthDayArray[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
	if (month == 2 && isLeapYear(year))
		return 29;
	else
		return monthDayArray[month];
}

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

重载<= > >= !=

我们已经写好了<==的重载,建议直接复用这两个来实现

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

重载>

复用<=的重载即可

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

建议复用<,可以少调用一个函数

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

重载>=

复用<的重载即可

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

复用<=也可以,但是多调用一个函数

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

重载!=

复用==

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

因为上面4个函数都比较短小,所以建议直接定义到类里面。

重载+= + -= -天数

由于+=是直接在原对象上+,所以我们先实现+=

重载+=

让一个日期对象+=天数。

思路是直接把day加到_day上,如果_day不合法,那么就进位,如果月份不合法,就继续进位。

为了支持连续的+=,要有返回值。

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

👆:如果有人传入了负数的day呢?

传入负数的day_day -= day有可能小于1,也进不去下面的while循环进行处理,最终导致结果错误。当然-=也面临同样的问题,

注意到+=一个负数就是-=这个负数的相反数,我们接下来实现-=,然后让它们相互复用即可。

重载+

复用+=,并且不能改变原对象,

这里就拷贝构造一个中间对象ret,返回ret必须传值返回,因为ret出作用域会被销毁。

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

不建议先写+的重载,然后重载+=的时候复用+,因为+肯定是需要拷贝构造的,如果+=复用了+,那么+=也要调用拷贝构造,影响效率。

重载-= -

同上,先实现-=-直接复用-=

_day -= day,如果_day不合法,则需要向前借位。

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

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

重载++ --

前置++和后置++都是单操作数,它们的重载要如何区分呢?

要区分就需要构成函数重载,C++语法规定,增加一个int类型额外参数的是后置++(注意:额外参数必须是int类型)

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

调用的时候++d等价于d.operator++()d++等价于d.operator++(0),编译器会自动传入一个额外参数表示调用后置++。这个参数本身没有意义,所以形参部分可以省略变量名,只写int

小知识:通过++和–的重载我们可以发现,前置的效率高一些,因为后置多了两个拷贝构造。

重载-(计算日期间隔天数)

我们再重载一个-,只不过这里是日期减日期,表示两个日期间隔天数。

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

👆:先看左右操作数哪个更大,并用flag控制正负号,让小日期一直++到大日期,统计的次数即为两个日期的间隔天数。

完整代码

.h

#pragma once
#include <iostream>
#include <cassert>
using std::cout;
using std::cin;
using std::endl;

class Date
{
public:
	bool isLeapYear(int year)
	{
		return year % 4 == 0 && year % 100 != 0 || year % 400 == 0;
	}
	int GetMonthDay(int year, int month);
	Date(int year = 1, int month = 1, int day = 1);

	void Print() const
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}

	bool operator==(const Date& d) const;
	bool operator<(const Date& d) const;

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

	Date& operator-=(int day);
	Date operator-(int day) const;

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

	int operator-(const Date& d) const;

private:
	int _year;
	int _month;
	int _day;
};

.cpp

#include "Date.h"

int Date::GetMonthDay(int year, int month)
{
	assert(year >= 1 && month >= 1 && month <= 12);
	static int monthDayArray[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
	if (month == 2 && isLeapYear(year))
		return 29;
	else
		return monthDayArray[month];
}

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

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 ((_year < d._year)
		|| (_year == d._year && _month < d._month)
		|| (_year == d._year && _month == d._month && _day < d._day));
}

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

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

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

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

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

还有流插入<<和流提取>>的重载本篇文章未实现。因为涉及到友元,我们将在下篇作为例子实现。

  • 7
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

世真

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值