龙空技术网

C++系列_4:实现一个日期类

deeplearning爱好者 295

前言:

现时兄弟们对“c语言__date__”大致比较着重,小伙伴们都需要了解一些“c语言__date__”的相关知识。那么小编也在网上汇集了一些对于“c语言__date__””的相关资讯,希望同学们能喜欢,同学们一起来了解一下吧!

一:前言

C++类和对象里面的默认成员函数,如构造函数,拷贝构造赋值重载这些问题虽然说是不难,但是想要写出一个标准的类还是具有一定挑战性的。而编写日期类是一个很好的练手项目

主要可以分为两类,一类就是构造函数,析构函数,拷贝构造等,一类运算符的重载等问题。

二:准备(1)实现文件

主要分为三个文件

Date.h中存放的是关于类的声明Date.cpp中存放的是关于函数的实现Test.cpp中用于逻辑控制(2)基本布局

Date.h中的类及需要实现的函数声明如下,其中有些声明为什么要这样写,将在本文后面具体讲述

对应位置可跳转

序号

题目

跳转

注意事项

1-1

构造函数

点击跳转

1.函数声明和实现不要一样 2.实现获取天数函数

1-2

拷贝构造

点击跳转

注意this指针

1-3

析构函数

点击跳转

浅拷贝与深拷贝

2-0

赋值运算符重载

点击跳转

this指针

2-1

日期+=天数

点击跳转

实现逻辑问题

2-2

日期+天数

点击跳转

注意复用,和+=的区别

2-3

日期-=天数

点击跳转

实现逻辑问题

2-4

日期-天数

点击跳转

注意复用,和-=的区别

2-5

前置++和后置++

点击跳转

前置++和后置++的区别

2-6

前置–和后置–

点击跳转

前置–和后置–的区别

3-1

==重载

点击跳转

注意复用

3-2

>重载和其它重载

点击跳转

注意复用

4-1

日期相减

点击跳转

实现逻辑

三:具体实现(1)默认成员函数实现

关于C++默认成员函数,都在下文中详细叙述过了,请移步

默认成员函数

A:全缺省构造函数

前言

全缺省构造函数如果没有传入实参将会用默认设置地形参,如果实际传入了实参,形参将会使用使用实参。对于日期类有点特殊,有如下限制

年份可以无限大,但是不能小于0月份最小是1,最大是12每月的天数有28,29,30,31这四种选择,但本质最特殊还是二月,闰年29天,平年28天

进行条件限制时,1,2两条可以很好的实现,可以写成if(year>0 && month>0 && month <13 && day>0)<但是对于第三条就麻烦了。

所以我们必须实现一个获得当月天数的函数,其实不止是这个构造函数要用,后面的很多函数都要用,所以也可以将其设置为内联函数

关于这个函数实现逻辑有很多,这里推荐一个比较优秀的方法:

定义一个数组,内含13条数据,下标为0的那个数据定义为0,剩余的下标对应的数据依次设置为下标所对应的月份的天数,其中下标为2的先默认设置为28。比如下标为3代表3月,那么对应数据为31。然后利用外部传入的月份,挑出对应元素,接着判断如果是2月同时是闰年,那么天数+1。

缺省构造函数实现

有了上述函数,根据构造函数定义及注意事项可实现如下

Date::Date(int year, int month, int day){	if (year >= 0 && month > 0 && month < 13 && day>0 && day < GetMonthDay(year, month))	{		_year=year;		_month=month;		_day=day;	}	else		std::cout << "数据错误" << std::endl;}
B:拷贝构造

拷贝构造函数是构造函数的重载形式。所以很简单,注意this指针

Date d2(d1)相当于Date d2=d1

Date::Date(const Date& d){	_year = d._year;	_month = d._month;	_day = d._day;}
C:析构函数

前文说过,对于日期类其实现的是浅拷贝,而对于某些带有的指针应该是深拷贝,所以对于日期类即便析构函数不写也是没有问题的

(2)赋值运算符重载

关于C++赋值运算符重载,都在下文中详细叙述过了,请移步

默认成员函数

需要明白的一点是赋值赋值运算符重载究竟用来干嘛?对于内置类型,我们做以下操作,编译器是肯定知道的

int main(){	int i=1;	int j=2;		i+=1;	j=i+1;	i-=1;	j=i-1;	i++;	++i	i--;	--i;	}

但是对于自定义类型,比如今天要实现的日期类,如果进行如上的运算符操作,编译器是不可能知道的,这其中的逻辑就要我们自己实现,让这些赋值运算符能够对咋们的自定义类型操作,以达到“看见这个表达式就知道什么意思”的用途

这其中,不管是哪种元素符,都一个非常基础的运算符,那就是赋值号,它的重载如下

Date& Date::operator=(const Date& d){	if (this != &d)	{		_year = d._year;		_month = d._month;		_day = d._day;	}	return *this;}
A:日期+=天数

如果是2021-2-23+=2,那么其结果为2021-2-25

注意返回值为什么是return *this

如果是2021-2-23+=10,那么其结果为2021-2-33,但是这样的结果肯定是不合理的。所以一旦出现不合理,用加之后的结果减去当月的合理天数,比如这里就是33-28=5,然后月份+1,则其结果为2021-3-5

如果是2021-2-23+=60,那么其结果为2021-2-83,那么按照之前步骤,先执行一次为2021-3-55,仍然不合理继续计算为2021-4-24

如果是2021-12-30+=20,那么这显然要跨年了,注意月份就要归为1,年份向前走,其结果为2022-1-9

Date& Date::operator+=(int day){	_day = _day + day;	while(_day > GetMonthDay(_year, _month))	{		_day=_day-GetMonthDay(_year,_month);		_month ++;		if(_month==13)		{			_month = 1;			_year++;		}	}	return *this;}
B:日期+天数

关于+=+很多人都分不清楚

int main(){	int i=0;	int j=0;	i+=1;//i变了	i+1//不变		j=i+1;//i不变,j变了}

所以调用这个函数,要保证自身不可变化,最终使用值返回结果。所以可以写拷贝构造一个,这就相当于int i=1,然后int j=i,然后return j=i+1。虽然可以套用上面代码,但是最巧妙的做法是复用+=

Date Date::operator+(int day){	Date ret(*this);	ret += day;	return ret;}
C:日期-=天数

如果是2021-2-23-=2,那么其结果为2021-2-21

如果是2021-2-23-=30,那么其结果为2021-2-(-7),但是这样的结果肯定是不合理的。所以一旦出现不合理,首先月份-1,然后天数加上此月的合理天数,那么最后结果为,2021-1-24

如果是2021-2-23-=60,其结果为2021-2-(-37),那么月份-1加上1月的合理天数变为2021-1-(-6),仍然不合理,那么月份继续-1(注意年份),加上12月的合理天数变为2020-12-25

//日期-=天数Date& Date::operator-=(int day){	_day = _day - day;	while (_day < 0)	{		_month--;		_day = _day + GetMonthDay(_year, _month);		if (_month == 0)		{			_month = 13;			_year--;		}	}	return *this;}
D:日期-天数

这个也就不用多说了

Date Date::operator-(int day){	Date ret(*this);	ret -= day;	return ret;}
E:前置++和后置++

前置运算符和后置运算符是C语言中讨论的较为多的话题,一个比较正确的解释是前置++是先++后 其他操作,后置++是先操作最后再++

那么对于一个自定义类型来说++d1,就要返回引用,因为是自身先要++,同样d1++,就要返回加后的结果然后再自身++

但是进行重载时,怎么区分呢。这里规定(除了this外)使用前置++无任何形参,使用后置++则表表明形参做以区别。只是为了区别,其实形参的值是不影响结果的。

//前置++Date& Date::operator++(){	(*this) += 1;	return *this;}//后置++Date Date::operator++(int){	Date ret(*this);	(*this) += 1;	return ret;}
F:前置–和后置–

这个就不用多说了

//前置--Date& Date::operator--(){	(*this) -= 1;	return *this;}//后置++Date Date::operator--(int){	Date ret(*this);	(*this) -= 1;	return ret;}
(3)运算符重载

关于C++运算符重载,都在下文中详细叙述过了,请移步

默认成员函数

A:==重载和!=重载

这是一个判断,两个日期是否相等的问题,非常简答,注意!===的复用

//==重载和!=重载bool Date::operator==(const Date& d){	return _year == d._year 		   && _month == d._month 		   && _day == d._day;}bool Date::operator!=(const Date& d){	return !((*this) == d);}
B:>重载和其他重载

只要完成>重载,那么其他的重载全部依靠复用即可完成

写>重载的逻辑非常简单

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

那么剩余的重载直接复用即可

//<重载 就是大于和等于取反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);}
(4)日期相减

日期相减其实是一个比较麻烦的问题,需要考虑的情况非常多。有一个非常好的思路是:找到两个日期中小的那一个,然后让小的日期逐渐递增的大的日期。

标签: #c语言__date__