尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

日期类实现(接类和对象联系一) - 实践

日期类实现(接类和对象联系一) - 实践
📅 发布时间:2026/6/19 3:54:46

日期类实现(接类和对象联系一) - 实践

我们要实现日期的加减、大小比较等功能。

一、声明

这里先创建一个头文件来进行定义声明:

Date(int year = 1900, int month = 1, int day = 1);
void Print();

创建Date对象时初始化日期(年、月、日)

void Print();作为普通成员函数,用于日期打印。

	int GetMonthDay(int year, int month){assert(month > 0 && month < 13);static int monthDayArray[13] = { -1, 31, 28, 31, 30, 31, 30,//0月为-1,没有
31, 31, 30, 31, 30, 31 };if (month == 2 && (year % 4 == 0 && year % 100 != 0) || (year% 400 == 0)){return 29;}else{return monthDayArray[month];}return monthDayArray[month];}bool CheckDate();

我们创建一个月份函数GetMonthDay,用于判断每个月固定的天数是多少,并且判断是否为润年。在这因为要频繁调用月份函数,所以建议使用静态数组减少开销。

CheckDate是用于判断,输入的日期是否有误。

//声明定义分离
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);
Date& operator+=(int day);
Date operator+(int day);
Date& operator-=(int day);
Date operator-(int day);
Date& operator++();
Date operator++(int);
int operator-(const Date& d);

上面这些就是我们要实现的功能,operator 作为专门用于重载运算符的关键字,我们在这给她先声明。

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

这里就是声明我们的年月日了。

//友元函数声明后可以访问私有函数:
friend ostream& operator<<(ostream& out, const Date& d);
friend istream& operator>>(istream& in, Date& d);
ostream& operator<<(ostream& out, const Date& d);
istream& operator>>(istream& in, Date& d);

上面是流的重载方便我们进行日期输入和输出,friend 是友元函数的定义,使用友元函数可以访问私有成员函数。

值得注意的是,流的重写不能放在类中,否则会因为内置的隐藏参数this指针导致会以奇怪的方式实现原功能。

下面就是完整的头文件Date.h:

#pragma once
#include
using namespace std;
#include
class Date
{//友元函数声明后可以访问私有函数:friend ostream& operator<<(ostream& out, const Date& d);friend istream& operator>>(istream& in, Date& d);
public:Date(int year = 1900, int month = 1, int day = 1);void Print();//获取某一年某一天//GetMonthDay没有声明和定义分离,默认是inline函数,对于这种小而频繁调用的函数设为内联函数减少开销int GetMonthDay(int year, int month){assert(month > 0 && month < 13);static int monthDayArray[13] = { -1, 31, 28, 31, 30, 31, 30,//0月为-1,没有
31, 31, 30, 31, 30, 31 };//因为频繁调用月分,直接放入静态区节省开销//闰年判断if (month == 2 && (year % 4 == 0 && year % 100 != 0) || (year% 400 == 0)){return 29;}else{return monthDayArray[month];}return monthDayArray[month];}bool CheckDate();//判断是否输入错误日期//声明定义分离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);Date& operator+=(int day);Date operator+(int day);Date& operator-=(int day);Date operator-(int day);Date& operator++();Date operator++(int);//这里有++d1和d1++两种形态,所以规定实现++重载的是前置++d1,后置++重载需要添加一个int形参,入d1.operator(0)int operator-(const Date& d);
private://在重载流要使用这三个得设为全局函数,或者增加三个公有函数//或者用另一种方法,友元元声明int _year;int _day;int _month;
};
//流重载
//流重载若放在成员函数里,会不可避免的出现左右操作数要按顺序对齐,导致原本cout<>(istream& in, Date& d);

二、实现

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

函数打印,输出我们输入的日期。

bool Date::CheckDate()
{if (_month < 1 || _month > 12|| _day < 1 || _day > GetMonthDay(_year, _month))//不属于一到十二月,和1到月底日数{return false;}else{return true;}
}
Date::Date(int year, int month, int day)
{_year = year;_month = month;_day = day;if (!CheckDate()) {cout << "非法日期:" << endl;Print();}
}

实现我们的年份判断,当月份和天数不在GetMonthDay规定的对应月份所拥有的天数中和月份不在1-12月时,返回false,并且打印出错误日期。

bool Date:: operator < (const Date& d) {if (_year < d._year) {return true;//比较年}else if (_year == d._year) {//年相等比较月if (_month < d._month) {return true;}else if (_month == d._month) {if (_day < d._day) {return true;}}else {return false ;}}
}

我们开始实现小于比较,通过逐步对比年月日来进行判断。

bool Date:: operator <= (const Date& d) {return *this < d || *this == d;}bool Date:: operator >= (const Date& d) {return *this > d || *this == d;}
bool Date:: operator > (const Date& d) {//return *this < d || *this == d;return !(*this <= d);}
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);
}

这里可以使用复用简化代码来实现他们的功能,但要注意的是必须有一个运算符按照<符的方式重写,否则会调入无线调用的死循环里。

Date& Date::operator+=(int day)
{	if (day < 0) {return *this -= (-day);}_day += day;while (_day > GetMonthDay(_year, _month)){_day -= GetMonthDay(_year, _month);++_month;//进入下一个月if (_month == 13)//如果出现13月{++_year;//进入下一年,月份归为一月_month = 1;}}return *this;
}
Date Date::operator+(int day)
{Date tmp = *this;tmp += day;//函数复用,不用费那么多脑细胞再写一个逻辑//tmp._day += day;//+不能改变自身值//while (tmp._day > GetMonthDay(tmp._year, tmp._month))//如果天数大于当前年月的天数//{//	tmp._day -= GetMonthDay(tmp._year, tmp._month);//当前天数减去当前月份的最大天数//	++tmp._month;//进入下一个月//	if (tmp._month == 13)//如果出现13月//	{//		++tmp._year;//进入下一年,月份归为一月//		tmp._month = 1;//	}//}return tmp;
}
Date Date::operator-(int day)
{Date tmp = *this;tmp -= day;return tmp;////-=复用-// Date tmp = *this;//if (day < 0)//获取需要减少的天数,入-50天//{//	return *this += -day;//}//tmp._day -= day;//当前天数减去需要减去的天数//while (tmp._day <= 0)//剩余天数小于0,退回上一个月,月数不足退回去年的12月,加上回退月数所有的天数,继续减去剩余天数,直到为正。//{//	--tmp._month;//	if (tmp._month == 0)//	{//		tmp._month = 12;//		tmp._year--;//	}//	// 借上⼀个⽉的天数//	tmp._day += GetMonthDay(tmp._year, tmp._month);// return tmp;一次拷贝//}
}
Date& Date::operator-=(int day)
{if (day < 0)//获取需要减少的天数,入-50天,则转换为正{return *this += -day;}_day -= day;//当前天数减去需要减去的天数while (_day <= 0)//剩余天数小于0,退回上一个月,月数不足退回去年的12月,加上回退月数所有的天数,继续减去剩余天数,直到为正。{--_month;if (_month == 0){_month = 12;_year--;}// 借上⼀个⽉的天数_day += GetMonthDay(_year, _month);}//-=复用-,相比-复用-=这里会多拷贝三次,增加开销.前两次是调用operator-=时两次拷贝和一次赋值拷贝//*this = *this - day;//相当于*this -= day;return *this;
}

这里实现了+、+=、-、-=四个运算符的重载,值得注意的是,在-和-=重写我注释掉了另外一种重写方法,两种方法是谁先重写就先复用谁,但是第二种方法会花费更大的开销。

//d1++ ;括号里数字随意,是整数就行
Date Date::operator++(int) {Date tmp = *this;*this += 1;return tmp;
}
//++d1
Date& Date::operator++() {*this += 1;return *this;
}

然后++的重载有些特殊,因为++分为++d和d++,在C++里规定,d++这种后置写法,重写需要带上参数,否则调用的是++d的重写方法。

//日期相减
int Date::operator-(const Date& d)
{Date max = *this;Date min = d;int flag = 1;if (*this < d){max = d;min = *this;flag = -1;}int n = 0;while (min != max){++min;++n;}return n * flag;
}

这里我们传入两个日期,当max日期小于min日期时进行交换,揉捏在进行循环增加小的日期,直到两日期相等,我们则将重复增加多少次的次数返回,得到的就是两日期相差天数。flag是用于判断起始日期差是增加还是减少。

ostream& operator<<(ostream& out, const Date& d)
{out << d._year << "年" << d._month << "⽉" << d._day << "⽇" << endl;return out;
}
istream& operator>>(istream& in, Date& d)
{while (1) {cout << "请依次输⼊年⽉⽇:>";in >> d._year >> d._month >> d._day;if (!d.CheckDate()){cout << "⽇期⾮法" << endl;d.Print();cout << "重新输入" << endl;}}return in;
}

这里就是我们重写的输入输出流,方便我们直接输入年月日 并且以更直观的方式输出。

Date.cpp:

#include"Date.h"
bool Date::CheckDate()
{if (_month < 1 || _month > 12|| _day < 1 || _day > GetMonthDay(_year, _month))//不属于一到十二月,和1到月底日数{return false;}else{return true;}
}
Date::Date(int year, int month, int day)
{_year = year;_month = month;_day = day;if (!CheckDate()) {cout << "非法日期:" << endl;Print();}
}
void Date::Print()
{cout << _year << "-" << _month << "-" << _day << endl;
}
bool Date:: operator < (const Date& d) {if (_year < d._year) {return true;//比较年}else if (_year == d._year) {//年相等比较月if (_month < d._month) {return true;}else if (_month == d._month) {if (_day < d._day) {return true;}}else {return false ;}}/*return !(*this>=d);*///这里不用下面方式是因为至少有一个上述比较,否则会出现无限调用·死循环
}
//以此类推
bool Date:: operator <= (const Date& d) {return *this < d || *this == d;}bool Date:: operator >= (const Date& d) {return *this > d || *this == d;}
bool Date:: operator > (const Date& d) {//return *this < d || *this == d;return !(*this <= d);}
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);
}
//定义
Date& Date::operator+=(int day)
{	if (day < 0) {return *this -= (-day);}//当前天数加上输入天数_day += day;//这里是+=而不是=,原因在于改变了自身的值,+不能改变自身值while (_day > GetMonthDay(_year, _month))//如果天数大于当前年月的天数{_day -= GetMonthDay(_year, _month);//当前天数减去当前月份的最大天数++_month;//进入下一个月if (_month == 13)//如果出现13月{++_year;//进入下一年,月份归为一月_month = 1;}}return *this;
}
Date Date::operator+(int day)
{	//+可以使用拷贝构造Date tmp = *this;tmp += day;//函数复用,不用费那么多脑细胞再写一个逻辑//tmp._day += day;//+不能改变自身值//while (tmp._day > GetMonthDay(tmp._year, tmp._month))//如果天数大于当前年月的天数//{//	tmp._day -= GetMonthDay(tmp._year, tmp._month);//当前天数减去当前月份的最大天数//	++tmp._month;//进入下一个月//	if (tmp._month == 13)//如果出现13月//	{//		++tmp._year;//进入下一年,月份归为一月//		tmp._month = 1;//	}//}return tmp;
}
Date Date::operator-(int day)
{Date tmp = *this; //一次拷贝tmp -= day;return tmp;//一次拷贝////-=复用-// Date tmp = *this;//if (day < 0)//获取需要减少的天数,入-50天//{//	return *this += -day;//}//tmp._day -= day;//当前天数减去需要减去的天数//while (tmp._day <= 0)//剩余天数小于0,退回上一个月,月数不足退回去年的12月,加上回退月数所有的天数,继续减去剩余天数,直到为正。//{//	--tmp._month;//	if (tmp._month == 0)//	{//		tmp._month = 12;//		tmp._year--;//	}//	// 借上⼀个⽉的天数//	tmp._day += GetMonthDay(tmp._year, tmp._month);// return tmp;一次拷贝//}
}
Date& Date::operator-=(int day)
{if (day < 0)//获取需要减少的天数,入-50天,则转换为正{return *this += -day;}_day -= day;//当前天数减去需要减去的天数while (_day <= 0)//剩余天数小于0,退回上一个月,月数不足退回去年的12月,加上回退月数所有的天数,继续减去剩余天数,直到为正。{--_month;if (_month == 0){_month = 12;_year--;}// 借上⼀个⽉的天数_day += GetMonthDay(_year, _month);}//-=复用-,相比-复用-=这里会多拷贝三次,增加开销.前两次是调用operator-=时两次拷贝和一次赋值拷贝//*this = *this - day;//相当于*this -= day;return *this;
}//d1++ ;括号里数字随意,是整数就行Date Date::operator++(int) {Date tmp = *this;*this += 1;return tmp;}//++d1Date& Date::operator++() {*this += 1;return *this;}//前置后置推荐使用前置,后置多两次拷贝//日期相减int Date::operator-(const Date& d){Date max = *this;Date min = d;int flag = 1;if (*this < d){max = d;min = *this;flag = -1;}int n = 0;while (min != max){++min;++n;}return n * flag;}ostream& operator<<(ostream& out, const Date& d){out << d._year << "年" << d._month << "⽉" << d._day << "⽇" << endl;return out;}istream& operator>>(istream& in, Date& d){while (1) {cout << "请依次输⼊年⽉⽇:>";in >> d._year >> d._month >> d._day;if (!d.CheckDate()){cout << "⽇期⾮法" << endl;d.Print();cout << "重新输入" << endl;}}return in;}

相关新闻

  • 山西在职研究生培训机构综合评估:聚焦服务品质与特色课程竞争力深度剖析
  • 《R for Data Science (2e)》免费中文翻译 (第9章) --- Layers(1) - 教程
  • 2025年上海金蝶软件代理商推荐榜,聚焦服务品质与方案适配力深度剖析

最新新闻

  • 2026年6月昆明黄金回收行情 哪里回收黄金不被扣损耗 - 润富黄金回收
  • 2026芜湖奢侈品名包名表回收避坑攻略:持证专业鉴定门店,查验估价全程公开透明 - 鸿运名品
  • 2026年6月优秀的石墨吸收塔厂家推荐伊科思德石墨科技,CFD流场模拟优化塔内结构提升整体处理产能 - 品牌鉴赏师
  • 2026【济南市】防水补漏怎么选?各区持证商家实地勘测整理 - 防水资讯
  • 企业级混合大脑:构建可解释、可审计、可干预的AI决策系统
  • 从历史脉络与民族大义视角论两岸政权认知的统一逻辑

日新闻

  • 5分钟掌握Python进化算法:Geatpy高性能优化工具完全指南
  • Microchip 24AA044 EEPROM选型与应用全指南:从参数解析到实战编程
  • 华为的鸿蒙到底有多牛?为什么称作遥遥领先?

周新闻

  • 3步解锁iOS设备:applera1n激活锁绕过完全指南
  • 39 2026 人工智能证书终极盘点,普通人选 AI 证书可以从这些方向入手
  • Redis 暴露公网有多危险?从端口检查到补救步骤

月新闻

  • 【总结】入门篇:50句话让你记住架构核心概念
  • WeChatMsg技术方案解析:实现Mac微信数据自主管理的完整解决方案
  • WeChatMsg:革新性微信数据备份方案,打造你的专属数字记忆库

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号