@TOC
🍖前言
通过类和对象的基本学习,我们可以实现一个完整的日期类。本文探讨日期类如何实现。
Tip
关于内联函数:在类内定义的函数,如果代码量少的话,编译器会酌将其转换成内联函数,这样会在调用的地方直接展开,能够提高效率。
在这个日期类中,提高的效率不是很大,所以在本文将日期类的成员函数的声明和定义分离了。如果想写成内联,需要直接在类里面定义。(内联的声明和定义不能分离)
日期类的六大默认成员函数:
日期类的成员变量如下:
class
{
private:
	int _year;
	int _month
	int _day;
};一、🍖构造函数
由于日期类的成员变量只有年月日,且无其他的动态资源的申请,所以构造函数可直接实现:
// 获取某年某月的天数
int Date::GetMonthDay(int year, int month)
{
	if (year < 0 || year>9999)
	{
		perror("error year!");
		exit(-1);
	}
	if (month < 0 || month > 12)
	{
		perror("error month!");
		exit(-1);
	}
	static int DaysGet[13] = { 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 DaysGet[month];
	}
}
        //构造函数
Date::Date(int year = 1900, int month = 1, int day = 1)
	{
		if (year < 0 || year>9999 )
		{
			perror("error year!");
			exit(-1);
		}
		if (month < 0 || month > 12)
		{
			perror("error month!");
			exit(-1);
		}
		if (day < 0 || day > GetMonthDay(year, month))
		{
			perror("error day!");
			exit(-1);
		}
		_year = year;
		_month = month;
		_day = day;
	}Tip:
GetMonthDay() 函数是获取某年某月的天数,比如2023年5月,返回31天。
细节点1.
GteMonthDay的数组写成static是为了提高效率,因为GetMonthDay函数需要不断地调用,所以这个数组只需要创建一次即可,不需要重复创建。
细节点2. 在判断闰年部分,最开始先要判断月份是否为2月,(因为闰年只跟2月有关)再判断是否为闰年即可提高效率。
二、🍖拷贝构造函数
日期类不需要动态申请空间资源,所以这里的拷贝构造函数可写可不写,因为如果我们不写,编译器生成的拷贝构造函数会自己完成内置类型的浅拷贝。
Date::Date(const Date& d)
{
	this->_year = d._year;
	this->_month = d._month;
	this->_day = d._day;
}三、🍖析构函数
由于日期类没有动态资源空间的申请,不需要实现析构函数。
四、🍖日期类的赋值运算符重载
Date& Date::operator=(const Date& d)
{
	//防止自己给自己赋值,可以判断一下
	//注意&d的&是取地址的意思
	if (this != &d)
	{
		this->_year = d._year;
		this->_month = d._month;
		this->_day = d._day;
	}
	return *this;
}Tip: 1.赋值运算符重载的返回类型是Date&,为了达到连续赋值的效果,且能提高效率。 2.参数设置为const Date&,是因为赋值前后右值未发生改变,使用const修饰更安全,且传引用可以提高效率,减少调用构造函数或拷贝构造函数的次数。
五、🍖取地址运算符重载
返回对象的地址即可。(在日期类没有特别大的意义)
const Date* Date::operator&()
{
	return this;
}六、🍖const取地址运算符重载
返回对象的地址即可。(在日期类没有特别大的意义)
const Date* Date::operator&() const
{
	return this;
}Tip: 取地址运算符重载和const取地址运算符重载区别在于this指针是否被const修饰, 一个是 Datethis, 一个是 const Datethis,它们构成函数重载。
日期类的关系操作符重载
在日期类中,只要实现
1.>运算符重载和==运算符重载
或者
2.<运算符重载和==运算符重载
即可完成其他的关系运算符重载。
一、🍖>运算符重载
1.先判断年是否大于 2.如果年大于,再判断是否月大于 3.如果年和月都大于,再判断日是否大于
以上三种情况均返回真,其余情况返回假
bool Date::operator>(const Date& d)
{
	if (_year > d._year)
	{
		return true;
	}
	if (_year == d._year && (_month > d._month))
	{
		return true;
	}
	if ( (_year == d._year) && (_month > d._month) && (_day > d._day) )
	{
		return true;
	}
	return false;
}二、🍖==运算符重载
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 || *this == d;
}四、🍖<运算符重载
<运算符重载又可以复用>=运算符重载,只需要取反方向即可。
bool Date::operator < (const Date& d)
{
	return !(*this >= d);
}五、🍖<=运算符重载
<=运算符重载又可以复用>运算符重载,只需要取反方向即可。
bool Date::operator <= (const Date& d)
{
	return !(*this > d);
}六、🍖!=运算符重载
!=运算符重载又可以复用 ==运算符重载,只需要取反方向即可。
// !=运算符重载
bool Date::operator != (const Date& d)
{
	return !(*this == d);
}日期类和天数的操作
一、🍖日期+=天数
日期+=天数,就是让日期本身变了。 比如:2023年1月1日+10天后,原来的日期变成了2023年1月11日。 返回原来的对象,但是对象的成员变量已被修改。
具体实现: 给定一个天数,首先让对象的天数加上,如果对象的天数大于当月的天数,就让对象的天数给减掉当月的天数,然后让月份进一位,然后判断月份是否大于12,如果月份大于12,让年进一位,同时让月份回到1月,如此循环。
最后返回*this,即返回自身。
//+=就是让自己变了
Date& Date::operator+=(int day)
{
	//如果天数<0,意思就是
	if (day < 0)
	{
		return *this -= -day;
	}
	_day += day;
	while (_day > GetMonthDay(_year, _month))
	{
		_day -= GetMonthDay(_year, _month);
		_month += 1;
		if (_month > 12)
		{
			_year += 1;
			_month = 1;
		}
	}
	return *this;
}Tip: 需要注意的点:如果日期小于0,相当于+=一个小于0的数,即 -= 一个大于0的数,可以复用下面实现的-=运算符重载。
二、🍖日期+天数
直接复用+=即可。 但是要注意的是,日期+天数返回的是一个新的对象,不是返回原来的对象。 所以需要在函数内部新建一个对象,该新的对象调用它拷贝构造函数拷贝原来的对象,+=天数后返回新对象。
Date Date::operator+(int day)
{
	//可以复用 +=
	Date tmp(*this);
	tmp.operator+=(day);
	return tmp;
}三、🍖日期-=天数
整体实现方法与+=类似,但需要注意的是: -=时,天数应该与上一个月的天数进行比较,因为-的是上个月的天数。
首先将对象的日期减掉天数,如果对象日期小于0,那就需要从上一个月来借用天数了。 回到上一个月先需要--_month,才可以回到上个月,--对象的月份后,如果月份小于1了,就需要从年借一年,--对象的年,然后让月份 = 12,如此循环。
Date& Date::operator-=(int day)
{
	if (day < 0)
	{
		return *this += -day;
	}
	_day -= day;
	while (_day < 1)
	{
		--_month;
		if (_month < 1)
		{
			--_year;
			_month = 12;
		}
		_day += GetMonthDay(_year, _month);
	}
	return *this;
}Tip: 需要注意的点:如果日期小于0,相当于-=一个小于0的数,即 +=一个大于0的数,可以复用+=运算符重载。
四、🍖日期-天数
复用-=即可,与日期+天数类似,返回的是一个新的对象,不改变原对象。
// 日期-天数
Date Date::operator-(int day)
{
	//可以复用 -=
	Date tmp(*this);
	tmp.operator-=(day);
	return tmp;
}五、🍖日期-日期
日期-日期的思想在于,先假设被减数是大的日期,减数是小的日期,如果假设不成立,那就交换一下顺序。
如果是小-大,那么返回日期的负数,表示回退多少多少天。
计算天数方法:从小的日期开始,不断累加,直到加到和大的日期相等即可。
在此期间会调用++运算符重载,在下面会实现。
//日期-日期
int Date::operator-(const Date& d)
{
	//小-大返回负数,大-小返回正数
	Date max = *this;
	Date min = d;
	int flag = 1;
	//如果假设错误,交换一下
	if (max < min)
	{
		max = d;
		min = *this;
		flag = -1;
	}
	int days = 0;//计算天数差
	while (min != max) //!=更高效, <还要比年月日
	{
		++days;
		++min;
	}
	return  flag * days;
}日期类++和--操作
一、🍖前置++
复用+=运算符重载即可。
Date& Date::operator++()
{
	//this->operator+=(1);
	*this += 1;
	return *this;
}二、🍖后置++
注意前置++和后置++是构成函数重载的,为了区分,我们给后置++一个参数类型,该参数不需要用到,所以给不给参数名都可以。
一般来说,尽量使用前置++,因为后置++返回的是++之前的对象,需要新建立一个对象,返回再返回该对象,新建立对象会调用拷贝构造函数,返回该对象又会给临时空间调用拷贝构造函数,效率较低。
Date Date::operator++(int)
{
	Date tmp(*this);
	//this->operator+=(1);
	tmp += 1;
	return tmp;
}三、🍖前置--
--操作的解释与++操作相似。
// 前置--
Date& Date::operator--()
{
	//this->operator-=(1);
	*this -= 1;
	return *this;
}四、🍖后置--
--操作的解释与++操作相似。
Date Date::operator--(int)
{
	Date tmp(*this);
	//tmp.operator-=(1);
	tmp -= 1;
	return tmp;
}整体代码(Date.h 和Date.cpp)
Date.h文件
class Date
{
	//成员函数如果不放在public里面,默认就是私有的
public:
	// 全缺省的构造函数
	Date(int year = 1900, int month = 1, int day = 1);
	// 获取某年某月的天数
	int GetMonthDay(int year, int month);
	void Print()
	{
		printf("%d %d %d\n", _year, _month, _day);
	}
	//本质上是:
	//void Print(Date* const this)
	
	//d1.Print()   本质上是:
	//d1.Print(&d1);
	// 拷贝构造函数
  // d2(d1)
//必须传引用,如果传值调用,会导致无限递归
	Date(const Date& d);
	//取地址运算符重载
	const Date* operator&();
	//const取地址运算符重载
	const Date* operator&() const;
	//函数声明
	//赋值运算符重载
	Date& operator=(const Date& d);
	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);
	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);
	//如果不设置成友元函数
	// d << cout ,特别别扭,因为this指针是指向d的,传参穿的是cout流插入符号
	ostream& operator<<(ostream& out);
	//这两个构成重载
	const Date* operator&();
	const Date* operator&() const;
private:
	int _year;
	int _month;
	int _day;
};Date.cpp文件
#define _CRT_SECURE_NO_WARNINGS 1
#include"Date.h"
// 获取某年某月的天数
int Date::GetMonthDay(int year, int month)
{
	if (year < 0 || year>9999)
	{
		perror("error year!");
		exit(-1);
	}
	if (month < 0 || month > 12)
	{
		perror("error month!");
		exit(-1);
	}
	static int DaysGet[13] = { 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 DaysGet[month];
	}
}
//构造函数
Date::Date(int year = 1900, int month = 1, int day = 1)
{
	if (year < 0 || year>9999)
	{
		perror("error year!");
		exit(-1);
	}
	if (month < 0 || month > 12)
	{
		perror("error month!");
		exit(-1);
	}
	if (day < 0 || day > GetMonthDay(year, month))
	{
		perror("error day!");
		exit(-1);
	}
	_year = year;
	_month = month;
	_day = day;
}
//拷贝构造
Date::Date(const Date& d)
{
	this->_year = d._year;
	this->_month = d._month;
	this->_day = d._day;
}
// 析构函数
//日期类不需要析构
//~Date();
// 赋值运算符重载
Date& Date::operator=(const Date& d)
{
	//防止自己给自己赋值,可以判断一下
	//注意&d的&是取地址的意思
	if (this != &d)
	{
		this->_year = d._year;
		this->_month = d._month;
		this->_day = d._day;
	}
	return *this;
}
//取地址运算符重载
const Date* Date::operator&()
{
	return this;
}
//const取地址运算符重载
const Date* Date::operator&() const
{
	return this;
}
// 日期+=天数
//+=就是让自己变了
Date& Date::operator+=(int day)
{
	if (day < 0)
	{
		return *this -= -day;
	}
	_day += day;
	while (_day > GetMonthDay(_year, _month))
	{
		_day -= GetMonthDay(_year, _month);
		_month += 1;
		if (_month > 12)
		{
			_year += 1;
			_month = 1;
		}
	}
	return *this;
}
// 日期+天数
Date Date::operator+(int day)
{
	//可以复用 +=
	Date tmp(*this);
	tmp.operator+=(day);
	return tmp;
}
// 日期-=天数
Date& Date::operator-=(int day)
{
	if (day < 0)
	{
		return *this += -day;
	}
	_day -= day;
	while (_day < 1)
	{
		--_month;
		if (_month < 1)
		{
			--_year;
			_month = 12;
		}
		_day += GetMonthDay(_year, _month);
	}
	return *this;
}
// 日期-天数
Date Date::operator-(int day)
{
	//可以复用 -=
	Date tmp(*this);
	tmp.operator-=(day);
	return tmp;
}
//日期-日期
int Date::operator-(const Date& d)
{
	//小-大返回负数,大-小返回正数
	Date max = *this;
	Date min = d;
	int flag = 1;
	//如果假设错误,交换一下
	if (max < min)
	{
		max = d;
		min = *this;
		flag = -1;
	}
	int days = 0;//计算天数差
	while (min != max) //!=更高效, <还要比年月日
	{
		++days;
		++min;
	}
	return  flag * days;
}
// >运算符重载
//实现一个大于和一个等于就可以完成其他所有的操作了
bool Date::operator>(const Date& d)
{
	if (_year > d._year)
	{
		return true;
	}
	if (_year == d._year && (_month > d._month))
	{
		return true;
	}
	if (_year == d._year && (_month > d._month) && _day > d._day)
	{
		return true;
	}
	return false;
}
// ==运算符重载
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 || *this == d;
}
// <运算符重载
bool Date::operator < (const Date& d)
{
	return !(*this >= d);
}
// <=运算符重载
bool Date::operator <= (const Date& d)
{
	return !(*this > d);
}
// !=运算符重载
bool Date::operator != (const Date& d)
{
	return !(*this == d);
}
// 前置++
Date& Date::operator++()
{
	//this->operator+=(1);
	*this += 1;
	return *this;
}
// 后置++
Date Date::operator++(int)
{
	Date tmp(*this);
	//this->operator+=(1);
	tmp += 1;
	return tmp;
}
// 前置--
Date& Date::operator--()
{
	//this->operator-=(1);
	*this -= 1;
	return *this;
}
// 后置--
Date Date::operator--(int)
{
	Date tmp(*this);
	//tmp.operator-=(1);
	tmp -= 1;
	return tmp;
}
//友元函数
// Date d 最好加const
//ostream& operator<<(ostream& out, const Date d)
//{
//	out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
//	return out;
//}
// d << cout
//不能加const,加了报错,不能限制流的改变
ostream& Date::operator<<(ostream& out)
{
	out << _year << "年" << _month << "月" << _day << "日" << endl;
	return out;
}🍖总结
本文实现了一个具体的日期类及其功能。









