yo!这里是日期类的简单实现

目录

前言

默认构造函数

常见运算符重载

==运算符重载

!=运算符重载

>运算符重载

>=运算符重载

<运算符重载

<=运算符重载

+=运算符重载

+运算符重载

-=运算符重载

-运算符重载(日期减若干天)

前置++运算符重载

后置++运算符重载

-运算符重载(两个日期相减)

流插入&流提取

后记


前言

        在初步对类和对象的学习之后,编写一个日期类对小伙伴们来说应该不是大问题,也可以当作是对类和对象的细节知识点的复习,巩固记忆,当然,本文重点是编写日期类中常见运算符重载,其次是如何完整编写一个类,那么,日期类的一些该有的功能具体有哪些呢?包括但不限于下方功能,快来跟我实现它们吧!

yo!这里是日期类的简单实现_第1张图片

yo!这里是日期类的简单实现_第2张图片

默认构造函数

        参考笔者之前的文章http://t.csdn.cn/ZgU0z,再根据日期类的成员变量的特点,建议自己写构造函数,析构函数、拷贝构造、赋值运算符重载、取地址操作符重载使用系统自带的即可。

        CheckDate()是检查日期是否合法,为方便调用直接封装成了一个函数;

        Date()是构造函数;

        Print()打印日期,实现功能时方便观察;

        GetMonthDay()获取某年某月的天数,后面多次会用到,所以等装成了一个函数;

        其他成员函数即为下方即将要实现的运算符重载。

代码:

#include 
#include 
using namespace std;

//时间类
class Date
{
public:
	bool CheckDate()
	{
		if ((_year > 0) && (_month > 0 && _month <= 12) && (_day >= 0 && _day <= GetMonthDay(_year, _month)))
		{
			return true;
		}
		return false;
	}

        Date(int year, int month, int day)
	{
		this->_year = year;
		this->_month = month;
		this->_day = day;

		assert(CheckDate());
	}

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

	//获取某年某月的天数(频繁调用)
	int GetMonthDay(int year, int month)   //放在类里默认内联
	{
		static int month_days[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 }; //static保证不用重复开空间
		int days = month_days[month];
		if ((month==2)&&((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
		{
			days++;
		}
		return days;
	}

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

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

	Date& operator++();//前置
	Date operator++(int);//后置
	Date& operator--();//前置
	Date operator--(int);//后置

	int operator-(const Date& d);

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

常见运算符重载

        为了增加代码的可读性,c++引入了运算符重载,与函数重载类似,也有返回值,函数名及参数列表,语法为 返回值类型 operator操作符(参数列表)

注意:

        ①重载后的操作符含义不能改变;

        ②操作符重载作为成员函数时,有一个隐藏参数——this指针;

        ③不能重载一些奇怪的符号,比如:¥......

  • ==运算符重载

        对于内置类型,直接使用==判断是否相等即可,但对于自定义类型,需要自己写出判断规则,即年、月、日同时相等。

代码:

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

测试:

yo!这里是日期类的简单实现_第3张图片

  • !=运算符重载

        对于内置类型,直接使用!=判断是否不相等即可,但对于自定义类型,需要自己写出判断规则,即年、月、日只要有一个不相等就是不相等。

        但是,我们已经写过了==的运算符重载,所以这里使用复用的方法,直接使用==判断,然后取反。

代码:

//方法一:
//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);
}

测试:

yo!这里是日期类的简单实现_第4张图片

  • >运算符重载

        根据日期类的特点,比较两个对象的大小,即先比较年份大小,相等情况下比较月份大小,月份相等情况下,比较日份的大小。

代码:

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

测试:

yo!这里是日期类的简单实现_第5张图片

  • >=运算符重载

        对于>=运算符的重载,可以在>运算符的基础之上加上==的判断条件即可,但是考虑一下复用的方法,既然已经写了>和==的重载,>=的重载是不是很容易就搞定了。

代码:

bool Date::operator>=(const Date& d)
{
	return (*this > d) || (*this == d);
}
  • <运算符重载

        根据上面使用复用的地方,这里是不是也可以使用复用的方法,直接调用>=判断,再取反即可。

代码:

bool Date::operator<(const Date& d)
{
	return !(*this >= d);
}
  • <=运算符重载

        <=运算符也考虑使用复用的方式。

        这里可以理解一下为什么先写==运算符的重载,是不是在写完一个逻辑运算符之后,所有逻辑运算符采用复用的方法都可以紧接着写出来,所以在后续编写其他的类时,一定要思考先重载哪种运算符对其他重载更有效更有利。

代码:

bool Date::operator<=(const Date& d)
{
	return !(*this > d);
}
  • +=运算符重载

        前面的运算符的操作数是两个对象,这里的+=是一个对象,另外一个是需要加上的天数,按照+=的原生逻辑,相加之后的值赋给对象,所以这里返回引用。

        思路:先将天数加到日份上,判断超过此时月份的天数,就向月份上进一,进一之后判断月份是否超过12,超过就向年份上进一,类似于两个数相加,个位数超过十了就像十位数上进一,十位数超过十了就向百位数上进一。

        注意:使用循环判断每一次操作之后的年月日是否合法,因为一开始加上的天数可能涉及好几个月或者好几年。

代码:

Date& Date::operator+=(int x_day)
{
	if (x_day < 0)   //判断若传入的天数是负数,表示的含义就是减去那么些天,就调用-=运算符
	{
		return *this -= -x_day;
	}
	this->_day += x_day;
	while (this->_day > this->GetMonthDay(this->_year, this->_month))
	{
		this->_day -= this->GetMonthDay(this->_year, this->_month);
		this->_month++;
		if (this->_month > 12)
		{
			this->_month = 1;
			this->_year++;
		}
	}
	return *this;
}

测试:

yo!这里是日期类的简单实现_第6张图片

  • +运算符重载

        与+=运算符类似,但相加的结果不赋给对象,这里返回Date类,而不是引用,就是返回一个拷贝,不改变原对象,为了可以使用复用的方法使用到+=,先将元对象拷贝给一个临时对象,将临时对象加上天数,再返回这个临时对象。

代码:

Date Date::operator+(int x_day)
{
    if (x_day < 0)
	{
		return *this - (-x_day);
	}
	Date tmp(*this);
	tmp += x_day;
	return tmp;
}
  • -=运算符重载

         -=运算符与+=运算符重载的实现类似,只不过将日份减去需要减的天数之后,如果小于0了就向月份借一,此时判断月份有无小于0,小于0就向年份借一。

代码:

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

测试:

yo!这里是日期类的简单实现_第7张图片

  • -运算符重载(日期减若干天)

        -运算符重载的实现参考+运算符重载的实现。

代码:

Date Date::operator-(int x_day)
{
    if (x_day < 0)
	{
		return *this + x_day;
	}
	Date tmp(*this);
	tmp -= x_day;
	return tmp;
}
  • 前置++运算符重载

        前置++特点是先++后使用,所以这里先复用+=运算符,将日份加上一,然后直接引用返回。

代码:

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

测试:

yo!这里是日期类的简单实现_第8张图片

  • 后置++运算符重载

        后置++特点是先使用后++,所以这里将原对象拷贝给临时对象,将原对象的日份加上一后,返回临时对象。

        注意:这里的参数列表多了个int类型,是为了形成重载形式,区别于前置++。

        前置--与后置--运算符的重载实现与++类似,这里不再赘述。

代码:

Date Date::operator++(int)
{
	Date tmp(*this);
	(*this) += 1;
	return tmp;
}
  • -运算符重载(两个日期相减)

        我们知道,两个日期相加是没有现实意义的,所以不会去专门实现两个日期相加,但两个日期相减是有现实意义的,表示相差的天数,返回int类型,-运算符需要两个操作数,除了一个*this,还需再传入一个对象,此对象不需发生变化,所以加const修饰,传引用是为了提高效率。

        思路:找到两个日期中较大的日期和较小的日期,让小日期++去接近大日期,++的次数就是相差的天数。

代码:

int Date::operator-(const Date& d)
{
	int days = 0;
	Date bigDate = *this;
	Date smallDate = d;
	int flag = 0;   //标志,判断原本是大日期减小日期还是小日期减大日期,决定着天数是正数还是负数
	if (*this < d)
	{
		bigDate = d;
		smallDate = *this;
		flag = 1;
	}
	while (smallDate < bigDate)
	{
		bigDate -= 1;
		days++;
	}

	return flag == 0 ? days : (-days);
}

测试:

yo!这里是日期类的简单实现_第9张图片

yo!这里是日期类的简单实现_第10张图片

  • 流插入&流提取

        在c++中,输入输出使用cin、cout,对于内置类型,iostream库中有对应函数可以适配实现内置类型的输入输出,但对于自定义类型,除了可以写成员函数print()打印,想用cin、cout就得编写对应重载函数。

        查阅文档发现,cin是istream类的对象,cout是ostream的对象,思考一下是重载成全局函数还是类成员函数?

        如果重载成成员函数,则是void Date::operator<<(istream& in){ ... },当我们调用时就是Date d; d<

        但是,重载成全局函数的话,我们访问不了私有的成员变量,所以这里我们使用友元,将全局函数设置成Date类的友元函数,即在类中加上

friend ostream& operator<<(ostream& out, const Date& d);

friend istream& operator>>(istream& in, Date& d);

代码:

//流插入
inline ostream& operator<<(ostream& out, const Date& d)  //注意:这里要加上const,否则像cout<<(d+100);中的d+100是常变量就会报错
{
	out << d._year <<"年" << d._month <<"月" << d._day <<"日" << endl;
	return out;
}

//流提取
inline istream& operator>>(istream& in, Date& d)
{
	in >> d._year >> d._month >> d._day;
	assert(d.CheckDate());
	return in;
}

 测试:

yo!这里是日期类的简单实现_第11张图片

后记

        在学习过c++的入门知识点以及类和对象的知识点之后,自主去实现日期类是对这些知识点的一个运用,将零零散散的知识点聚集在一起,给自己查缺补漏,非常的使用,有时间的小伙伴可以去尝试实现一遍,又不太清楚的可以在评论区讨论或者私我,拜拜! 


你可能感兴趣的:(c语言,c++,职场和发展,visualstudio,后端)