【C++】日期类的实现

在这里插入图片描述

个人主页:@Weraphael
✍作者简介:目前学习C++和算法
✈️专栏:课设
希望大家多多支持,咱一起进步!
如果文章对你有帮助的话
欢迎 评论 点赞 收藏 加关注✨


前景回顾

  • 知识回顾

这篇博客将运用以上知识来实现一个完整的日期类

目录

  • 前景回顾
  • 一、准备工作
  • 二、实现内容(Date.h)
  • 三、代码实现(Date.cpp)
    • 3.1 三个没必要写的成员函数
    • 3.2 构造函数
    • 3.3 打印成员函数
    • 3.4 运算符重载之判断日期是否相等 (==)
    • 3.5 运算符重载之比较日期大小(<)
    • 3.6 运算符重载之比较日期大小 (>=)
    • 3.7 运算符重载之比较日期大小 (<=)
    • 3.8 运算符重载之比较日期大小 (>)
    • 3.9 运算符重载之判断日期是否相当 (!=)
    • 3.10 日期+=天数
    • 3.11 日期-=天数
    • 3.12 日期+天数
    • 3.13 日期 - 天数
    • 3.14 前置++
    • 3.15 后置++
    • 3.16 前置- -
    • 3.17 后置- -
    • 3.18 日期 - 日期
  • 四、流提取>>
  • 五、流插入<<
  • 五、完整代码

一、准备工作

为了方便管理,我们可以创建多个文件来实现

  • Test.cpp — 测试代码逻辑 (源文件)
  • Date.cpp — 逻辑实现 (源文件)
  • Date.h — 存放函数的声明 (头文件)
    【C++】日期类的实现_第1张图片

二、实现内容(Date.h)

#include 
using namespace std;

class Date
{
public:
	// 友元函数
	//friend void operator>>(istream& in, Date& x); 错误(返回类型)
	friend istream& operator>>(istream& in, Date& x);
	friend ostream& operator<<(ostream& out, const Date& x);

	// 写构造函数
	// 全缺省(注意:声明给缺省值)
	Date(int year = 1, int month = 2, int day = 3);

	// 打印成员函数
	void Print() const
	{
		cout << this->Year << '-' << this->Month << '-' << this->Day << endl;
	}
	
	// 以下是运算符重载
	
	// == 
	bool operator==(const Date& x) const;

	// <
	bool operator<(const Date& x) const;

	// >=
	bool operator>=(const Date& x) const;

	// <=
	bool operator<=(const Date& x) const;

	// >
	bool operator>(const Date& x) const;

	// !=
	bool operator!=(const Date& x) const;

	// 获取某月的天数
	int GetMonthDay(int Year, int Month);

	// 日期+=天数
	Date& operator+=(int day);

	// 日期-=天数
	Date& operator-=(int day);

	// 日期+day(返回的变量销毁了,不能用引用返回)
	Date operator+(int day) const;
	
	// 日期-day
	Date operator-(int day) const;
	
	// 前置++()
	Date& operator++();
	
	// 后置++
	Date operator++(int);

	// 前置--  
	Date& operator--();

	// 后置--
	Date operator--(int);

	// 日期-日期 = 天数(int)
	int operator-(const Date& x) const;
	
private:
	int Year; // 年
	int Month; // 月
	int Day; // 日
};

//流提取
istream& operator>>(istream& in, Date& x);

// 流插入
ostream& operator<<(ostream& out, const Date& x);

三、代码实现(Date.cpp)

3.1 三个没必要写的成员函数

  • 析构函数

成员类型都是内置类型,出了作用域,其变量自动销毁。因此可以不写(但编译器会自动生成,不用管)

  • 拷贝构造函数

编辑器的默认拷贝构造函数对内置类型是直接拷贝的

  • 赋值操作符重载函数

编辑器的默认赋值操作符重载函数对内置类型也是直接拷贝的

3.2 构造函数

构造函数和以上三个成员函数不同,因为如果不自己定义构造函数,内置类型的默认构造函数是随机值

// 构造函数(声明给缺省值)
Date::Date(int year, int month, int day)
{
	this->Year = year;
	this->Month = month;
	this->Day = day;
}

注意:

  • 当全缺省参数声明和定义分离的情况下,声明给缺省值
  • 类也可以让声明和定义分离。类声明放在.h文件中,成员函数定义放在.cpp文件中,同时需要使用作用域限定符::

3.3 打印成员函数

void Print() const
{
	cout << this->Year << '-' << this->Month << '-' << this->Day << endl;
}

3.4 运算符重载之判断日期是否相等 (==)

// == 
bool Date::operator==(const Date& x) const
{
	return (Year == x.Year)
		&& (Month == x.Month)
		&& (Day == x.Day);
}

3.5 运算符重载之比较日期大小(<)

// <
bool Date::operator<(const Date& x) const
{
	// 年份大的则大
	if (Year < x.Year) 
		return true;
	// 月份大的则大(前提:月份相同)
	else if (Year == x.Year && Month < x.Month) 
		return true;
	// 日份大的则大(前提:年月份相同)
	else if (Year == x.Year && Month == x.Month && Day < x.Day) 
		return true;
	// 若不满足以上要求,则为假
	else 
		return false;
}

当以上<==写完后,以下代码都可以复用了(3.6 ~ 3.9)

3.6 运算符重载之比较日期大小 (>=)

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

3.7 运算符重载之比较日期大小 (<=)

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

3.8 运算符重载之比较日期大小 (>)

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

3.9 运算符重载之判断日期是否相当 (!=)

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

3.10 日期+=天数

// 获取某月的天数
int Date::GetMonthDay(int Year, int Month)
{
	// 因为GetMonthDay函数会掉用很多次,每次调用都会为GetDay开辟空间
	// 因此static修饰的变量就不会再每次调用函数的时候开辟变量空间,减少损耗
	static int GetDay[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30,31,30,31 };
	// 可能存在在闰年的情况
	if (Month == 2 && Year % 400 == 0 || Year % 4 == 0 && Year % 100 != 0)
	{
		return 29;
	}
	return GetDay[Month];
}

// 日期+=天数
//d1 += 2
Date& Date::operator+=(int day)
{
	if (day < 0)
	{
		return *this -= -day;
	}
	
	this->Day += day;
	// 可能会超过月份天数的最大值
	while (this->Day > GetMonthDay(this->Year, this->Month))
	{
		// 超过就减当月的最大值
		this->Day -= GetMonthDay(this->Year, this->Month);
		// 然后月份进位
		this->Month++;
		// 如果月份进位超过12
		if (this->Month > 12)
		{
			// 月份轮回
			this->Month -= 12;
			// 年份进位
			this->Year++;
		}
	}
	return *this;
}

日期+=天数返回的结果是给到对象本身。当生命周期结束后,*this(对象)不会被销毁,因此可以使用引用返回

3.11 日期-=天数

Date& Date::operator-=(int day)
{
	if (day < 0)
	{
		return *this += -day;
	}
	
	this->Day -= day;
	// 减去的日期可能存在负数日期
	while (this->Day <= 0)
	{
		// 月份减一(借位)
		this->Month--;
		// 可能存在月份借完的情况
		if (this->Month <= 0)
		{
			this->Month += 12;
			// 年份借位
			this->Year--;
		}
		this->Day += GetMonthDay(this->Year, this->Month);
	}
	return *this;
}

3.12 日期+天数

// 日期+day
Date Date::operator+(int day) const
{
	// 下面是拷贝构造函数,不是赋值运算符重载
	Date tmp = *this; 
	
	/*tmp.Day += day;
	while (tmp.Day > GetMonthDay(tmp.Year, tmp.Month))
	{
		tmp.Day -= GetMonthDay(tmp.Year, tmp.Month);
		tmp.Month++;
		if (tmp.Month > 12)
		{
			tmp.Month -= 12;
			tmp.Year++;
		}
	}
	return tmp;*/
	
	// 可以直接复用(最好)
	return tmp += day;
}

日期+天数只是计算出一个结果,并没有改变其对象。所以可以用一个临时变量tmp来存储结果。但千万不能使用引用返回,因为当生命周期结束,变量tmp销毁,可能会产生随机值。

3.13 日期 - 天数

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

	return tmp -= day;
}

3.14 前置++

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

3.15 后置++

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

前置++·后置++会发生一个问题:函数名会相同。因此,C++规定:后置(++/--)重载时多增加一个int类型的参数,但调用函数时该参数不用传递。

3.16 前置- -

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

3.17 后置- -

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

3.18 日期 - 日期

// 思路:循环计数的方法
int Date::operator-(const Date& x) const
{
	// 假设一开始日期1比日期大
	Date Max = *this;
	Date Min = x;
	int flag = 1;
	// 特判存在日期2比日期1大
	if (Min > Max)
	{
		Max = x;
		Min = *this;
		flag = -1;
	}
	int cnt = 0;
	while (Min != Max)
	{
		Min++;
		cnt++;
	}	
	return cnt * flag;
}

四、流提取>>

有的人说了,既然可以打印日期,那能否输入日期呢?其实是可以的。那么能否用cin或者printf函数呢?答案当然不可以。因为printf只能根据格式化输出来打印的,例如%d%f等内置类型,cout它是能自动识别类型的(其实可以自动识别类型是因为在库中实现了【如图1】),同样的只能识别内置类型。
【C++】日期类的实现_第2张图片

因此,为了能让自定函数也能使用到cincin就可以通过重载>>运算符来实现输出的操作:

>>有是一个双操作数:一个是日期类对象,另一个是cin。注意:cin是类对象,是istream的类对象。这个istream这个库定义的
【C++】日期类的实现_第3张图片

istream& operator>>(istream& in, Date& x)
{
	int year, month, day;
	in >> year >> month >> day;
	// 可能存在输入非法日期
	if (month > 0 && month < 13 && day > 0 && day <= x.GetMonthDay(year, month))
	{
		x.Year = year;
		x.Month = month;
		x.Day = day;
	}
	else
	{
		cout << "非法日期" << endl;
	}
	return in;

}
  • >>运算符重载不能成为成员函数的原因:
    成员函数的第一个参数默认是this指针,它是指向当前对象的指针,而这里的第一个参数是cin,因此不能变成成员函数,那就只能定义在全局了。但是这里有个问题,那就是全局的函数就不能访问类中私有的成员变量了,这里就用 友元函数 来解决此问题,即在函数声明前加上friend放在类中(详情见Date.h)
  • 返回值是istream&而不是void的原因:
    如果返回值是void,那它就不能连续输入。(cin >> d1 >> d2)因为流插入的顺序是从左向右的,cin >> d1后,返回的是void类型,再cin>>d2就会产生类型冲突,因此我们可以返回d1的类型来解决此问题,而cin的类型就是istream
  • 两个形参能否用const修饰:
    in>>d1in是每次都键盘上输入不能的内容,因此不能用const修饰;而d1再没获取数据之前可能是随机值,然后再通过键盘获取数据,也就改变了数据内容,因此不能用const修饰。

五、流插入<<

<<有是一个双操作数:一个是日期类对象,另一个是cout。注意:cout是类对象,是ostream的类对象。这个ostream这个库定义的
【C++】日期类的实现_第4张图片

ostream& operator<<(ostream& out, const Date& x)
{
	out << x.Year << "年" << x.Month << "月" << x.Day << "日" << endl;

	return out;  
}
  • out不能用const修饰的原因:
    out相当于终端控制台上,而每次数据输出在控制台都不一样,因此out不能用const修饰
  • 更多详细细节参考流提取>>

五、完整代码

【Date.h】

class Date
{
public:
	// 友元函数
	friend istream& operator>>(istream& in, Date& x);
	friend ostream& operator<<(ostream& out, const Date& x);

	// 写构造函数,因为内置类型的默认构造函数是随机值
	// 全缺省(注意:声明给缺省值)
	Date(int year = 1, int month = 2, int day = 3);
	
	// 不用写析构函数 - 没有向堆申请空间,出了作用域,变量自动销毁
	
	// 不用写拷贝构造函数 - 默认的拷贝构造函数对内置类型是直接拷贝的
	
	// 不用写赋值操作符重载函 - 其默认函数对内置类型也是直接拷贝的
	
	// 打印成员函数
	void Print() const
	{
		cout << this->Year << '-' << this->Month << '-' << this->Day << endl;
	}

	// == 
	bool operator==(const Date& x) const;

	// <
	bool operator<(const Date& x) const;

	// >=
	bool operator>=(const Date& x) const;

	// <=
	bool operator<=(const Date& x) const;

	// >
	bool operator>(const Date& x) const;

	// !=
	bool operator!=(const Date& x) const;

	// 获取某月的天数
	int GetMonthDay(int Year, int Month);

	// 日期+=天数
	Date& operator+=(int day);

	// 日期-=天数
	Date& operator-=(int day);

	// 日期+day(返回的变量销毁了,不能用引用返回)
	Date operator+(int day) const;
	
	// 日期-day(返回的变量销毁了,不能用引用返回)
	Date operator-(int day) const;
	
	// 前置++()
	Date& operator++();
	
	// 后置++
	Date operator++(int);

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

	// 日期-日期 = 天数(int)
	int operator-(const Date& x) const;
	
private:
	int Year; 
	int Month;  
	int Day;  
};

//流提取
istream& operator>>(istream& in, Date& x);

// 流插入
ostream& operator<<(ostream& out, const Date& x);

【Date.cpp】

#include "Date.h"

// 构造函数(声明给缺省值)
Date::Date(int year, int month, int day)
{
	this->Year = year;
	this->Month = month;
	this->Day = day;
}

// == 
bool Date::operator==(const Date& x)  const
{
	return (Year == x.Year)
		&& (Month == x.Month)
		&& (Day == x.Day);
}

// <
bool Date::operator<(const Date& x)  const 
{
	if (Year < x.Year) return true;
	else if (Year == x.Year && Month < x.Month) return true;
	else if (Year == x.Year && Month == x.Month && Day < x.Day) return true;
	else return false;
}

// 写出了 < 和 == 以下都可以复用

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

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

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

// !=
bool Date::operator!=(const Date& x) const
{
	return !(*this == x);
}

// 获取某月的天数
int Date::GetMonthDay(int Year, int Month)
{
	static int GetDay[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30,31,30,31 };
	if (Month == 2 && Year % 400 == 0 || Year % 4 == 0 && Year % 100 != 0)
	{
		return 29;
	}
	return GetDay[Month];
}

// 日期+=天数
Date& Date::operator+=(int day)
{
	if (day < 0)
	{
		return *this -= -day;
	}
	this->Day += day;
	while (this->Day > GetMonthDay(this->Year, this->Month))
	{
		this->Day -= GetMonthDay(this->Year, this->Month);
		this->Month++;
		if (this->Month > 12)
		{
			this->Month -= 12;
			this->Year++;
		}
	}
	return *this;
}

// 日期-=天数
Date& Date::operator-=(int day)
{
	if (day < 0)
	{
		return *this += -day;
	}
	this->Day -= day;
	while (this->Day <= 0)
	{
		// 月份减一(借位)
		this->Month--;
		// 可能存在月份借完的情况
		if (this->Month <= 0)
		{
			this->Month += 12;
			this->Year--;
		}
		this->Day += GetMonthDay(this->Year, this->Month);
	}
	return *this;
}

// 日期+day
Date Date::operator+(int day) const
{
	// 下面是拷贝构造函数,不是赋值运算符重载
	Date tmp = *this; 
	return tmp += day;
}

// 日期-day
Date Date::operator-(int day) const
{
	Date tmp = *this;
	return tmp -= day;
}

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

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

// 前置--  
Date& Date::operator--()
{
	*this -= 1;
	return *this;
}

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

// 日期1-日期2 = 天数(int)
int Date::operator-(const Date& x) const
{
	Date Max = *this;
	Date Min = x;
	int flag = 1;
	if (Min > Max)
	{
		Max = x;
		Min = *this;
		flag = -1;
	}
	int cnt = 0;
	while (Min != Max)
	{
		Min++;
		cnt++;
	}
	return cnt * flag;
}

// 流提取
istream& operator>>(istream& in, Date& x)
{
	int year, month, day;
	in >> year >> month >> day;
	if (month > 0 && month < 13 && day > 0 && day <= x.GetMonthDay(year, month))
	{
		x.Year = year;
		x.Month = month;
		x.Day = day;
	}
	else
	{
		cout << "非法日期" << endl;
	}
	return in;

}

//流插入
ostream& operator<<(ostream& out, const Date& x)
{
	out << x.Year << "年" << x.Month << "月" << x.Day << "日" << endl;

	return out;  
}

你可能感兴趣的:(课设,C++,c++,开发语言,学习,visualstudio,算法)