【C++】日期类的实现




日期类实现

一、自定义类型 判断运算符重载

因为都只是判断,并不修改,所以都加 const

(1)bool operator==(const Date& y) const;

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

(2)bool operator!=(const Date& y) const;

// d1 != d2  
// !=运算符重载
bool Date::operator!=(const Date& y) const
{
	//复用
	return !(*this == y);     //★ this不能显式写形参、实参,但可以显式使用 
}

★ this不能显式写形参、实参,但可以显式使用

(3)bool operator>(const Date& y) const;

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

	return false;
}



实现 > 和 == 剩下的都能通过 复用 来实现

(4)bool operator>=(const Date& y) const;

// >=运算符重载
//实现 > 和 == 剩下的都能通过 复用 来实现
bool Date::operator>=(const Date& y) const
{
	return *this > y || *this == y;
}

(5)bool operator<(const Date& y) const;

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

(6)bool operator<=(const Date& y) const;

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



二、日期类中功能的实现

重载哪些运算符,主要看这个运算符有没有意义。有意义就可以实现,没意义不实现

(一)获取某年某月的天数

// 获取某年某月的天数
int Date::GetMonthDay(int year, int month)
{
	assert(year >= 1 && month >= 1 && month <= 12);   // 容易出现不合法的日期,要加强检查

	//直接让下标对应月份,取出相应的天数即可  //没有必要用 switch,case,if,else 什么的
	int monthArray[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;
	
		return monthArray[month];
}


(二)多少天以后 是几月几号

(1) 通过 += 来实现 +

  1. 先用 += 算出对应的多少天以后是几月几号 【+= 改变原日期】
//一、用+=实现+ 
// d1 += 100 [ 先用 += 算出对应的多少天以后是几月几号 ]
Date& Date::operator+=(int day)
{   
	// 若day < 0,则复用operator-=
	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;
}
  1. 再用 += 来实现 +
    +和-,对原日期并不产生影响【 所以加 const 】,+=/-= 才是对传过来的*this有影响,才是要改变原日期
// +和-,对原日期并不产生影响,+=/-= 才是对传过来的*this有影响,才是要改变原日期
// tmp是局部变量,出了作用域就销毁了,所以不能用Date&传引用返回,只能传值返回
//1.通过+,用tmp返回
Date Date::operator+(int day) const
{
	Date tmp(*this); //拷贝构造tmp【 一个已存在的类 拷贝构造给 另一个新创建的类 】
	tmp += day;

	return tmp;
}

tmp是局部变量,出了作用域就销毁了,所以不能用Date&传引用返回,只能传值返回



(2) 通过 + 来实现 +=

  1. 再用 + 来实现 +=
Date Date::operator+(int day)const
{
   Date tmp(*this);  //+对原日期不影响,所以开辟新对象,来进行操作
   tmp._day += day;
   while (tmp._day > tmp.GetMonthDay(tmp._year, tmp._month))
   {
   	tmp._day -= tmp.GetMonthDay(tmp._year, tmp._month);

   	++tmp._month;
   	if (tmp._month == 13)
   	{
   		tmp._year++;
   		tmp._month = 1;
   	}
   }

   return tmp;
}
  1. 用 + 算出对应的多少天以后是几月几号
//二、用 + 实现 += [ 先用 + 算出对应的多少天以后是几月几号 ]
//2.通过 +=,直接对 *this 进行修改,Date& 进行返回
Date& Date::operator+=(int day)
{
	//是否还需要创建一个新类专门用来 +=   //不用,+= 本来就是在对原日期进行修改,所以直接对*this进行处理即可
	/*Date d = *this + day;
	*this = d;*/

	*this = *this + day; //注意:这里不能*this+=day; 这里本来就还在创建 += 这个变量,若用+=则会复用现在的operator+=
	                     //再通过 operator+ 来算出对应的多少天以后是几月几号
	return *this;
}

是否还需要创建一个新类专门用来 += ?
不用,+= 本来就是在对原日期进行修改,所以直接对 *this 进行处理即可



对比(1)和(2)

(1)+ 两次拷贝 _ 1. += 时的拷贝 2. return值(传值返回) 时的 临时拷贝返回

(2)+ = 直接对原数进行修改,然后传引用返回

  • 调用 +
    = 赋值也是一种拷贝 【拷贝是一个很耗性能的一个东西(要是拷贝的是一棵10w个节点的二叉树,则是一个巨大的工程,跟重建一棵二叉树 没差)】

  • + = 可以直接对this作更改,Date & 可传引用返回,不用传值拷贝返回,多调用+ = 便可



(三)多少天前 是几月几号( -= 实现 )

多用 += 和 -= 实现,可以传引用,提高效率

//多用+=和-=实现,可以传引用
Date& Date::operator-=(int day)
{
	if (day < 0)
	{
		return *this += (-day);
	}

	_day -= day;
	while (_day <= 0)
	{
		--_month;
		if (_month == 0)
		{
			_year--;        //月变化,也会影响到年 ,所以先确定好年月,才能GetMonthDay【因为GetMonthDay取决于你是否是闰年,以及月份,才能得到该年该月准确的MonthDay】
			_month = 12;
		}
		_day += GetMonthDay(_year, _month);
	}

	return *this;
}

(三)多少天前 是几月几号( - 实现 )

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

	return tmp;
}


(四)相差多少天【 倒计时:还剩多少天 】

// d1 - d2
int Date::operator-(const Date& d) const
{
	//假设左小右大
	int flag = 1;
	Date max = *this;
	Date min = d;

	//假设错了,左小右大
	if (*this < d)
	{
		max = d;
		min = *this;
		flag = -1;
	}

	// 1.直接让n一路++,直到min==max,n++到的就是相差的天数(思路更优,更简单)     // 2. 比较天 再比较月,年 =》 判断是否需要借位,借前一个月的天数,还是不够再借 
	int n = 0;
	while (min!=max)
	{
		++min;
		++n;
	}

	return n * flag;
}


(五)前置++

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


(六)后置++

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

	return tmp;   //tmp返回 => 返回类型 Date
}


(七)前置- -

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


(八)后置- -

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

	return tmp;   //tmp返回 => 返回类型 Date
}



输入输出 前言

成员函数的一大特点:参数中含有隐藏的 this指针

                 //隐藏的this指针
void Date::operator<<(ostream& out)
{
	out << _year << "年" << _month << "月" << _day << "日" << endl;
}
// 结果,运用<<操作符时,d << out 【 不符合可读性 】

结果,运用<<操作符时,d << out 【 不符合可读性 】



解决方式:

  1. 放域外,就不是成员函数,就没有隐藏的this指针,不需要操作符左边第一个参数一定得是d,而可以是out

  2. 但在域外无法访问类里的private私有成员变量 => 将其设置成 友元函数,就可以访问了

(九)输出日期

// 放域外,就不是成员函数,就没有隐藏的this指针,不需要操作符左边第一个参数一定得是d,而可以是out
// 但在域外无法访问类里的private私有成员变量 => 将其设置成 友元函数,就可以访问了

ostream& operator<<(ostream& out, const Date& d)  //输出不需要修改 对象d ,so 加const修饰
{
	out << d._year << "年" << d._month << "月"<< d._day << "日" << endl;

	return out;
}


(十)输入日期

istream& operator>>(istream& in, Date& d)  //输出要修改 so 不加const修饰
{
	in>> d._year >>  d._month >>  d._day;

	return in;
}


三、完整码源

(一)Date.h

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<assert.h>
using namespace std;

class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1);
	/*~Date();*/  //内置类型成员,并没有涉及资源开辟问题,销毁时不需要资源清理,最后系统直接将其内存回收即可; => 没有必要调用析构函数了

	void Print();
	// 获取某年某月的天数
	int GetMonthDay(int year, int month);

	//判断 运算符重载
	bool operator==(const Date& y) const;
	bool operator!=(const Date& y) const;
	bool operator>(const Date& y) const;
	bool operator<(const Date& y) const;
	bool operator>=(const Date& y) const;
	bool operator<=(const Date& y) const;

	//重载哪些运算符,主要看这个运算符有没有意义
	//有意义就可以实现,没意义不实现

	//day天后是几月几号
	Date& operator+=(int day);
	Date operator+(int day)const;

	//day天前是几月几号
	Date& operator-=(int day);
	Date operator-(int day)const;

	//倒计时:还剩多少天
	int operator-(const Date& d)const;

	Date& operator++();
	Date operator++(int);

	Date& operator--();
	Date operator--(int);

	
	//void operator << (ostream& out);


	//默认构造函数——取地址 以及 const取地址 操作符重载
	Date* operator&()
	{
		cout << "Date* operator&()" << endl;
		return (Date*)0x12233222;    //故意让别人知道
	}

	const Date* operator&()const
	{
		cout << "const Date* operator&()const" << endl;

		//return this;
		return nullptr;
	}


	//友元函数
	friend ostream& operator<<(ostream& out, const Date& d);    //输出到out,不改变d
	friend istream& operator>>(istream& in, Date& d);     

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


// 重载操作符必须有一个类类型参数->不能通过重载运算,改变内置类型的运算规则
// int operator+(int i, int j)

(二)Date.cpp

#include"Date.h"

Date::Date(int year, int month, int day)
{
	_year = year;
	_month = month;
	_day = day;

	// 容易出现不合法的日期,要加强检查
	if (_year < 1 ||
		_month < 1 || _month > 12 ||
		_day < 1 || _day > GetMonthDay(_year, _month))
	{
		Print();
		cout << "日期非法" << endl;         
	}
}

void Date::Print() {
	cout << _year << "/" << _month << "/" << _day << endl;
}


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


// d1 != d2  
// !=运算符重载
bool Date::operator!=(const Date& y) const
{
	//复用
	return !(*this == y);     //★ this不能显式写形参、实参,但可以显式使用 
}

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

	return false;
}


// >=运算符重载
//实现 > 和 == 剩下的都能通过 复用 来实现
bool Date::operator>=(const Date& y) const
{
	return *this > y || *this == y;
}


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


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


// 获取某年某月的天数
int Date::GetMonthDay(int year, int month)
{
	assert(year >= 1 && month >= 1 && month <= 12);   // 容易出现不合法的日期,要加强检查

	//直接让下标对应月份,取出相应的天数即可  //没有必要用 switch,case,if,else 什么的
	int monthArray[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;
	
		return monthArray[month];
}




//一、用+=实现+ 
// d1 += 100 [ 先用 += 算出对应的多少天以后是几月几号 ]
Date& Date::operator+=(int day)
{   
	// 若day < 0,则复用operator-=
	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;
}


// +和-,对原日期并不产生影响,+=/-= 才是对传过来的*this有影响,才是要改变原日期
// tmp是局部变量,出了作用域就销毁了,所以不能用Date&传引用返回,只能传值返回
//1.通过+,用tmp返回
Date Date::operator+(int day) const
{
	Date tmp(*this); //拷贝构造tmp【 一个已存在的类 拷贝构造给 另一个新创建的类 】
	tmp += day;

	return tmp;
}



二、用+实现+= [ 先用 + 算出对应的多少天以后是几月几号 ]
//
2.通过+=,直接对*this进行修改,Date&进行返回
//Date& Date::operator+=(int day)
//{
//	//是否还需要创建一个新类专门用来+=   //不用,+=本来就是在对原日期进行修改,所以直接对*this进行处理即可
//	/*Date d = *this + day;
//	*this = d;*/
//
//	*this = *this + day; //注意:这里不能*this+=day; 这里本来就还在创建 += 这个变量,若用+=则会复用现在的operator+=
//	                     //再通过 operator+ 来算出对应的多少天以后是几月几号
//	return *this;
//}
//
//
//Date Date::operator+(int day)const
//{
//	Date tmp(*this);  //+对原日期不影响,所以开辟新对象,来进行操作
//	tmp._day += day;
//	while (tmp._day > tmp.GetMonthDay(tmp._year, tmp._month))
//	{
//		tmp._day -= tmp.GetMonthDay(tmp._year, tmp._month);
//
//		++tmp._month;
//		if (tmp._month == 13)
//		{
//			tmp._year++;
//			tmp._month = 1;
//		}
//	}
//
//	return tmp;
//}


//对比 一、和 二、
//(1)`+` 两次拷贝 _ 1. += 时的拷贝    2. return值(传值返回) 时的 临时拷贝返回
//(2)`+ = ` 直接对原数进行修改,然后传引用返回

//调用 `+`
//`=` 赋值也是一种拷贝 【拷贝是一个很耗性能的一个东西(要是拷贝的是一棵10w个节点的二叉树,则是一个巨大的工程,跟重建一棵二叉树 没差)】
//`+ = ` 可以直接对this作更改,Date & 可传引用返回,不用传值拷贝返回,多调用`+ = ` 便可







//多用+=和-=实现,可以传引用
Date& Date::operator-=(int day)
{
	if (day < 0)
	{
		return *this += (-day);
	}

	_day -= day;
	while (_day <= 0)
	{
		--_month;
		if (_month == 0)
		{
			_year--;        //月变化,也会影响到年 ,所以先确定好年月,才能GetMonthDay【因为GetMonthDay取决于你是否是闰年,以及月份,才能得到该年该月准确的MonthDay】
			_month = 12;
		}
		_day += GetMonthDay(_year, _month);
	}

	return *this;
}


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

	return tmp;
}


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


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

	return tmp;   //tmp返回 => 返回类型 Date
}


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


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

	return tmp;   //tmp返回 => 返回类型 Date
}


// d1 - d2
int Date::operator-(const Date& d) const
{
	//假设左小右大
	int flag = 1;
	Date max = *this;
	Date min = d;

	//假设错了,左小右大
	if (*this < d)
	{
		max = d;
		min = *this;
		flag = -1;
	}

	// 1.直接让n一路++,直到min==max,n++到的就是相差的天数(思路更优,更简单)     // 2. 比较天 再比较月,年 =》 判断是否需要借位,借前一个月的天数,还是不够再借 
	int n = 0;
	while (min!=max)
	{
		++min;
		++n;
	}

	return n * flag;
}

                 //隐藏的this指针
//void Date::operator<<(ostream& out)
//{
//	out << _year << "年" << _month << "月" << _day << "日" << endl;
//}
// 结果,运用<<操作符时,d << out 【 不符合可读性 】


// 放域外,就不是成员函数,就没有隐藏的this指针,不需要操作符左边第一个参数一定得是d,而可以是out
// 但在域外无法访问类里的private私有成员变量 => 将其设置成 友元函数,就可以访问了
ostream& operator<<(ostream& out, const Date& d)
{
	out << d._year << "年" << d._month << "月"<< d._day << "日" << endl;

	return out;
}


istream& operator>>(istream& in, Date& d)
{
	in>> d._year >>  d._month >>  d._day;

	return in;
}

//Date::~Date()
//{
//}

(三)Test.cpp

  • 13
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值