【C++】C++实现Date日期类

基础框架

Date.h
其实因为日期类的成员变量都是内置数据类型,所以拷贝构造和析构都可以不写,但养成习惯还是可以写的,尽量写一下比较好

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

class Date
{
public:
	//构造函数
	//注意:缺省只写在声明
	Date(int year = 2023, int month = 2, int day = 4);

	//拷贝构造函数
	Date(const Date&d1);

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

Date.cpp

#include"Date.h"
//构造函数
Date::Date(int year, int month, int day)
{
	assert(month>0&&month < 13 || day <= GetMonthDay(year, month)&&day>0);
	_year = year;
	_month = month;
	_day = day;
}

//拷贝构造函数
Date::Date(const Date&d1)
{
	_year = d1._year;
	_month = d1._month;
	_day = d1._day;
}

注意:
1.构造函数的缺省值,只写在声明就好
2.构造函数需要检查传入的参数是否合理

比较运算符重载

首先我们实现一些比较的运算符
比如:==,!=,<,>等
因为上篇运算符重载的博文已写过一遍,这里接直接上代码了
Date.h

    //==运算符重载
	bool operator==(const Date&d);

	//d1<d2
	bool operator<(const Date&d);

	//d1<=d2
	bool operator<=(const Date&d);

	//d1>d2
	bool operator>(const Date&d);

	//d1>=d2
	bool operator>=(const Date&d);
	
	//d1!=d2
	bool operator!=(const Date&d);

Date.cpp

//==运算符重载
bool Date::operator==(const Date&d)
{
	return _year == d._year&&_month == d._month&&_day == d._day;
}

//d1<d2
bool Date::operator<(const Date&d)
{
	if (_year < d._year)
	{
		return true;
	}
	else if (_year == d._year&&_month < d._month)
	{
		return true;
	}
	else if (_year == d._year&&_month < d._month&&_day < d._day)
	{
		return true;
	}
	else
	{
		return false;
	}
}

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

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

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

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

比较运算符,在写完"==“和”<"时,再往下写,我们发现会有很多重复的代码,那么此时,我们就可以利用已写的运算符重载,实现代码的复用

赋值&递增递减运算符重载

1.赋值运算符重载

赋值运算符也较为简单,直接上代码

Date& Date::operator=(const Date&d)
{
	if (*this != d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;

	}

	return *this;
}

需要注意的时,可以会出现d1=d1的情况,所以我们可以在函数内部判断一下

2.递增递减运算符重载

一. 前置++和后置++

++和–函数实际是不需要参数的,但因为编译器需要区分前置和后置,所以设定了一个区分标准:
如果参数列表没有参数,则为前置,如果有int,则为后置
具体代码如下
Date.h

    //默认是前置++
	Date& operator++();

	//参数列表有整型为后置++
	Date operator++(int);

Date.cpp

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

	return *this;
}


Date Date::operator++(int)
{
	Date tmp = *this;
	*this += 1;

	return tmp;
}

因为后置++,是进行其他操作然后再++,则我们需要返回++前的Date,所以我们在函数内部创建局部变量保存++前的数据,并且返回。但因为是局部变量,所以返回值不能是引用

二. 前置–和后置–

原理同+

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

//参数列表有整型为后置--
Date Date::operator--(int)
{
	Date tmp = *this;

	*this -= 1;
	return tmp;
}

算术运算符重载

1.+&+=

因为日期加天数才有意义,所以我们设置参数是int
同时,再加完天数后,我们需要考虑是否要在月份进位,甚至年份进位。所以我们需要一个函数来获取当年当月的天数,来判断是否需要进位

Date.cpp

int Date::GetMonthDay(int year,int month)
{
	int Month[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
	//闰年的2月有29天
	if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0)
	{
		Month[2] = 29;
	}

	return Month[month];
}

+运算重载代码如下
注意:因为是+,所以原本的对象数据并不会改变,所以我们需要创建局部变量来进行操作并返回,因为是局部变量,所以返回值不能是引用

Date Date::operator+(int day)
{
	//普通写法
	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 == 13)
		{
			tmp._year++;
			tmp._month = 1;
		}
	}

	return tmp;
}

+=运算符重载

Date& Date::operator+=(int day)
{
	//普通写法
	_day += day;
	while (_day > GetMonthDay(_year ,_month))
	{
		_day -= GetMonthDay(_year, _month);
		_month++;
		if (_month == 13)
		{
			_year++;
			_month = 1;
		}
	}

	return *this;
}

那么这时候,我们发现两个函数的代码,有一段几乎相同,那么我们可考虑一方复用另一方

第一种:+复用+=

Date& Date::operator+=(int day)
{
	//普通写法
	_day += day;
	while (_day > GetMonthDay(_year ,_month))
	{
		_day -= GetMonthDay(_year, _month);
		_month++;
		if (_month == 13)
		{
			_year++;
			_month = 1;
		}
	}

	return *this;
}


Date Date::operator+(int day)
{
	//复用+=运算符重载
	Date tmp = *this;
	tmp += day;

	return tmp;
}

第二种:+=复用+

Date Date::operator+(int day)
{
	//普通写法
	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 == 13)
		{
			tmp._year++;
			tmp._month = 1;
		}
	}

	return tmp;
}

Date& Date::operator+=(int day)
{
	//复用+运算符重载
	*this = *this + day;

	return *this;
}

两种方法都可以完成功能,但二者的效率是有所差距的,第一种方法是更好的
因为我们发现,+两种方法都需要完成两次拷贝构造,所以效率相同,但是第二种方法的+=需要调用+和=,但第一种不需要,所以第一种效率更高

2.-和-=

-的逻辑和+差不多,不过-需要向月和年借天数

-=运算符重载如下

//d1-=100
Date& Date::operator-=(int day)
{
	_day -= day;
	while (_day <= 0)
	{
		_month--;
		if (_month == 0)
		{
			_year--;
			_month = 12;
		}
		_day += GetMonthDay(_year, _month);
	}

	return *this;
}

-运算符重载如下

//d1-100
Date Date::operator-(int day)
{
	Date tmp = *this;
	tmp -= day;

	return tmp;
}

但这时,我们发现一个问题,可能出现d1-= -100
这时,我们意识到,+和-的传参还需要分类正负

负负得正

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

	return *this;
}

Date& Date::operator-=(int day)
{
	if (day < 0)
	{
		*this += -day;
		return *this;
	}

	_day -= day;
	while (_day <= 0)
	{
		_month--;
		if (_month == 0)
		{
			_year--;
			_month = 12;
		}
		_day += GetMonthDay(_year, _month);
	}

	return *this;
}

3.日期 — 日期

日期减日期,应当返回两个日期之间相差的天数,可能出现正数或者负数

//日期减日期
int Date::operator-(const Date&d)
{
	//判断哪个日期比较大
	Date max = *this;
	Date min = d;
	int flag = 1;//默认是正的

	if (*this < d)
	{
		max = d;
		min = *this;
		flag = -1;
	}

	//n记录天数
	int n = 0;
	//max!=min的效率比min<max高
	while (max != min)
	{
		//前置效率更高
		++min;
		n++;
	}

	return n*flag;

}

流插入重载&流提取重载

cout和cin是iostream的两个类,cout在ostream,cin在istream。
对于内置数据类型,iostream头文件内是已经编写的

比如:

int a=0;
double b=1.11;

//cout.operator(a)
cout<<a<<endl;
//cout.operator(b)
cout<<b<<endl;

那么对于自定义数据类型,我们则需要自己编写

1.流插入重载

首先,如果我们编写在类内

//流插入重载
void Date::operator<<(ostream&out)
{
	out << _year << "年" << _month << "月" << _day << "日" << endl;
}

int main()
{
   Date d1(2023,2,8);
   //那么调用会是这样的
   //d1.operator<<(cout);
   d1<<cout;//不符合常规书写
}

我们发现函数调用时,书写形式和我们平常书写的不一样。
所以,流插入重载我们不能写在类内
那如果设为全局函数,又无法访问私有成员变量,将私有改成共有显然是不合适的,我们其实可以在类内定义获取私有成员变量的函数,比如GetYear,GetMonth等等,但C++更常用友元解决这个问题

一.友元

可以让类外函数访问私有属性

class Date
{
    //友元  关键字friend
	friend void operator<<(ostream&out, const Date&d);
public:
	//构造函数
	Date(int year = 2023, int month = 2, int day = 4);

	//拷贝构造函数
	Date(const Date&d1);
	
private:
	int _year;
	int _month;
	int _day;
};

//流插入重载
void operator<<(ostream&out, const Date&d)
{
	out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
}

然后,我们发现,会有cout<<d1<<d2<<endl;的情况,所以返回值我们应该设为ostream,可以继续调用重载。
又因为cout是全局,并且只能有一个,不能拷贝,所以需要加&

ostream& operator<<(ostream&out, const Date&d)
{
	out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
	return out;
}

2.流提取

代码如下

//流提取重载
istream& operator>>(istream&in, Date&d)
{
	in >> d._year >> d._month >> d._day;

	return in;
}

因为cin是要对对象进行修改的,所以不能加const。

内联函数的补充知识

在类内定义的成员函数,编译器可能会将其定为内联函数(看其是否经常调用且短小)

如果我们将流插入和流提取设置为内联函数,声明在Date.h中

在这里插入图片描述

在这里插入图片描述
会出现链接错误。
因为在Date.h中找这两个函数,发现是内联函数,就直接展开了,没有进行Date.cpp链接,所以函数调用失败。所以我们直接在Date,h中完成定义

结束语

本章用于记笔记,如果有不对或者不足的地方,欢迎大佬们指正,补充。感谢大家的阅读,如果感觉博主写的还可以,麻烦点个赞支持一下,阿里嘎多。
在这里插入图片描述

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值