C++类与对象的应用—日期计算器

news2024/12/29 10:29:37

目录

一、前言

二、日期类的实现

检查日期的合法性

< 运算符重载 

==运算符重载

<=运算符重载

>运算符重载

>=运算符重载

!=运算符重载

进一步优化 

日期+天数

日期+=天数 

日期-=天数 

日期-天数 

前置++&&后置++

前置--&&后置-- 

思路:

日期-日期 

三、总代码 

Date.h文件

Date.cpp文件


一、前言

在我们的日常生活中,我们可能需要计算几天后的日期或者计算日期差等,我们计算日期可以直接用日期计算器来求得,下面我们先看一下网络上面的日期计算器截图:

现在我们就用代码自己实现一个日期计算器。 


二、日期类的实现

检查日期的合法性

实现日期类的首要前提肯定就是先要检查日期的合法性。

class Date
{
public:
    bool isLeapYear(int year) //判断是否为闰年
    {
    	return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
    }
	int GetMonthDay(int year, int month)
	{
        assert(year >= 0 && month > 0 && month < 13);
        //加上static防止函数频繁调用开辟几十个字节大小的数组
		static int monthDayArray[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
		if (month == 2 && isLeapYear(year))
			return 29; //闰月29天
		else
			return monthDayArray[month];
	}
	Date(int year = 1, int month = 1, int day = 1)
	{
		if (year >= 1 && month <= 12 && month >= 1 && day <= GetMonthDay(year, month))
		{
            //确保日期合法
			_year = year;
			_month = month;
			_day = day;
		}
	}
private:
	int _year;
	int _month;
	int _day;
};

< 运算符重载 

  •  思路:

运算符重载我们在上篇博文中已经讲过,这里就不再过多赘述,现在我们要写出<运算符重载,假如我们实例化出对象d1和d2,我们比较d1是否小于d2,只需要考虑如下三种情况:

  • d1的年小于d2的年
  • d1与d2的年相等,d1的月小于d2的月
  • d1与d2的年和月都相等,d1的天小于d2的天

这三种情况任何一种情况符合,都属于d1小于d2。

  • 代码如下:
// <运算符重载
bool Date::operator<(const Date& d) const //类外访问成员函数需要设定类域
{
	if (_year < d._year ||
		_year == d._year && _month < d._month ||
		_year == d._year && _month == d._month && _day < d._day)
		return true;
	else
		return false;
}

==运算符重载

  •  思路:

==运算符重载只需要判定d1和d2的年、月、日对应是否相等。

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

<=运算符重载

  •  思路:

<=就是要么小于要么等于那么我们写<=运算符重载就可以复用前面所写的<运算符重载和<=运算符重载,就无需再写大量代码了。

  • 代码如下:
// <=运算符重载
bool Date::operator<=(const Date& d) const
{
	return *this < d || *this == d;
}

>运算符重载

  • 思路:

>的反义就是<=,所以我们只需要复用<=运算符重载再对其取反就可以了。

  • 代码如下:
// >运算符重载
bool Date::operator>(const Date& d) const
{
	return !(*this <= d);
}

>=运算符重载

  •  思路:

>=的反义就是<,所以我们只需要复用<运算符重载再对其取反就可以了。

  • 代码如下:
// >=运算符重载
bool Date::operator>=(const Date& d) const
{
	return !(*this < d);
}

!=运算符重载

  • 思路:

!=的反义就是==,所以我们只需要将==取反即可。

  • 代码如下:
//!=运算符重载
bool Date::operator!=(const Date& d) const
{
	return !(*this == d);
}

进一步优化 

  • 我们上面所写的运算符重载都是建立在声明和定义分离的,这里我们可以对其进行进一步的优化。我们以前学过内敛函数,并且对于类来说直接定义在类里面的函数默认为内敛函数,所以我们将代码量小的函数直接定义在类里面,代码量大的函数进行声明和定义分离。
  • Dath.h文件:
#pragma once

#include <iostream>
#include <assert.h>
using std::cout;
using std::cin;
using std::endl;

class Date
{
public:
	bool isLeapYear(int year)  //判断是否为闰年
	{
		return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
	}
	int GetMonthDay(int year, int month);//获取某年某月天数
	//构造函数
	Date(int year = 1, int month = 1, int day = 1);
	//打印
	void Print() const
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}

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

private:
	int _year;
	int _month;
	int _day;
};
  • Dath.cpp文件:
#include "Date.h"

//<运算符重载
bool Date::operator<(const Date& d)const
{
	if (_year < d._year ||
		_year == d._year && _month < d._month ||
		_year == d._year && _month == d._month && _day < d._day)
		return true;
	else
		return false;
}
//==运算符重载
bool Date::operator==(const Date& d)const
{
	return _year == d._year &&
		_month == d._month &&
		_day == d._day;
}
//获取某年某月天数
int Date::GetMonthDay(int year, int month)
{
	assert(year >= 0 && month > 0 && month < 13);

	static int monthDayArray[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
	if (month == 2 && isLeapYear(year))
	{
		return 29;
	}
	else
	{
		return monthDayArray[month];
	}
}
//构造函数
Date::Date(int year, int month, int day)
{
	if (year >= 1 &&
		month <= 12 && month >= 1 &&
		day >= 1 && day <= GetMonthDay(year, month))
	{
		_year = year;
		_month = month;
		_day = day;
	}
	else
	{
		cout << "日期非法" << endl;
	}
}

日期+天数

  • 思路:

日期+天数我们得到的还是一个日期,需要注意的就是进位的问题(天满了往月进,月满了往年进)

  • 加过天数后超过该月的最大天数,需要进位。
  • 当月进位到13后,年进位+1,月重置为1。
  • 法一:
Date Date::operator+(int day) const
{
	Date ret(*this); //拷贝构造,拿d1去初始化ret
	ret._day += day;
	while (ret._day > GetMonthDay(ret._year, ret._month))
	{
		ret._day -= GetMonthDay(ret._year, ret._month);
		ret._month++;
		if (ret._month == 13)
		{
			ret._year++;
			ret._month = 1;
		}
	}
	return ret;
}

出了作用域,对象ret就不在了,它是一个局部对象,我们这里不能用引用,用了的话,返回的就是ret的别名,但是ret又已经销毁了,这就有可能访问野指针,所以出了作用域,如果对象不在了,就不能用引用返回,要用传值返回。

  • 法二:复用日期+=天数

此种方法是建立在日期+=天数的基础上完成的,然后进行复用。

Date Date::operator+(int day) const
{
	//法二:复用日期 += 天数
	Date ret(*this);
	ret += day;
	return ret;
}

日期+=天数 

  • 法一:

前面我们实现了日期+天数,下面我们看函数的第一行,我们就调用了一个拷贝构造:

Date ret(*this); //拷贝构造,拿d1去初始化ret

这里调用拷贝构造是为了不在*this上面直接做改动,只在ret上面进行操作,其理由是日期+天数得到的还是另外一个日期,而不用拷贝构造直接在*this上面做改动只会导致原有的日期也发生变化,而这个变化也正是我日期+=天数的需求。

Date& Date::operator+=(int day) //传引用返回
{
	//如果day小于0,要单独处理
	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是全局的,出了作用域还在。

  • 法二:复用日期+天数
Date& Date::operator+=(int day)
{
	//法二:复用
	* this = *this + day;
	return *this;
}

⭐:法一和法二那个更优呢?

答案:法一讨论这个问题的本质就是去分析用+去复用+=好还是用+=复用+好。答案是用+去复用+=好,因为+有两次拷贝,而+=没有拷贝,所以实现+=,并且用+去复用+=效率更高。


日期-=天数 

  • 思路:

日期-=天数得到的还是一个日期。日期减去天数后要分析day是否大于0,只要大于0就没问题,如果小于0就需要借位。所以我们就需要进行讨论:

  1. 当减的天数为负数,则调用+=
  2. 若减后的day<0,则从月借位
  3. 若月为0,则从年借位,月重置为12
  • 代码如下:
Date& Date::operator-=(int day)
{
	//如果减去的天数是负数,要单独处理,直接调用+=运算符重载
	if (day < 0)
	{
		return *this += -day;
	}
	_day -= day;
	while (_day <= 0)
	{
		--_month;
		if (_month == 0)
		{
			_month = 12;
			--_year;
		}
		_day += GetMonthDay(_year, _month);
	}
	return *this;
}

日期-天数 

日期-天数我们直接复用日期-=天数即可:

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

前置++&&后置++

  • 思路:

C++里面有前置++和后置++,这也就导致了一个问题,我们该如何区别它们呢?C++规定:无参的为前置++,有参的为后置。

  • 前置++
//前置++
Date& Date::operator++() //无参的为前置
{
	*this += 1; //直接复用+=
	return *this;
}
  • 后置++
//后置++
Date Date::operator++(int i) //有参数的为后置
{
	Date tmp(*this);
	*this += 1; //复用+=
	return tmp;
}

前置--&&后置-- 

  • 思路:

前置--和后置--思路和前置++后置++一样。

  • 前置--
//前置--
Date& Date::operator--() //无参的为前置
{
	*this -= 1; //直接复用-=
	return *this;
}
  • 后置--
//后置--
Date Date::operator--(int i) //有参数的为后置
{
	Date tmp(*this);
	*this -= 1;
	return tmp;
}

日期-日期 

  • 思路:

日期 - 日期得到的是天数,首先我们得判断两个日期的大小,用min和max分别代表小的和大的日期,随后,算出min和max之间的差距,若min!=max,则min就++,随即定义变量n也自增++,最后返回n(注意符号)

  • 代码如下:
//日期 - 日期
int Date::operator-(const Date& d) const
{
	int flag = 1; //方便后续计算正负
	Date max = *this;
	Date min = d;
	if (*this < d)
	{
		min = *this;
		max = d;
		flag = -1; //计算正负
	} //确保max是大的,min是小的
	int n = 0;
	while (min != max)
	{
		min++;
		n++;
	}//算出min和max之间绝对值差距
	return n * flag; //如果d1大,结果为正,d2大结果为负
}

三、总代码 

Date.h文件

#pragma once

#include <iostream>
#include <assert.h>
using std::cout;
using std::cin;
using std::endl;

class Date
{
public:
	bool isLeapYear(int year)  //判断是否为闰年
	{
		return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
	}
	int GetMonthDay(int year, int month);//获取某年某月天数
	//构造函数
	Date(int year = 1, int month = 1, int day = 1);
	//打印
	void Print() const
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}

	//<运算符重载
	bool operator<(const Date& d) const;
	//==运算符重载
	bool operator==(const Date& d)const;
	//<=运算符重载
	bool operator<=(const Date& d)const
	{
		return *this < d || *this == d;
	}
	//>运算符重载
	bool operator>(const Date& d)const
	{
		return !(*this <= d);
	}
	//>=运算符重载
	bool operator>=(const Date& d)const
	{
		return !(*this < d);
	}
	//!=运算符重载
	bool operator!=(const Date& d)const
	{
		return !(*this == d);
	}
	Date& operator+=(int day);
	Date operator+(int day) const;
	Date& operator-=(int day);
	Date operator-(int day) const;

	//前置++
	Date& operator++() //无参的为前置
	{
		*this += 1; //直接复用+=
		return *this;
	}
	//后置++
	Date operator++(int i) //有参数的为后置
	{
		Date tmp(*this);
		*this += 1; //复用+=
		return tmp;
	}
	//前置--
	Date& operator--() //无参的为前置
	{
		*this -= 1; //直接复用-=
		return *this;
	}
	//后置--
	Date operator--(int i) //有参数的为后置
	{
		Date tmp(*this);
		*this -= 1;
		return tmp;
	}

	int operator-(const Date& d) const;
private:
	int _year;
	int _month;
	int _day;
};

Date.cpp文件

#include "Date.h"

//<运算符重载
bool Date::operator<(const Date& d)const
{
	if (_year < d._year ||
		_year == d._year && _month < d._month ||
		_year == d._year && _month == d._month && _day < d._day)
		return true;
	else
		return false;
}
//==运算符重载
bool Date::operator==(const Date& d)const
{
	return _year == d._year &&
		_month == d._month &&
		_day == d._day;
}
//获取某年某月天数
int Date::GetMonthDay(int year, int month)
{
	assert(year >= 0 && month > 0 && month < 13);

	static int monthDayArray[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
	if (month == 2 && isLeapYear(year))
	{
		return 29;
	}
	else
	{
		return monthDayArray[month];
	}
}
//构造函数
Date::Date(int year, int month, int day)
{
	if (year >= 1 &&
		month <= 12 && month >= 1 &&
		day >= 1 && day <= GetMonthDay(year, month))
	{
		_year = year;
		_month = month;
		_day = day;
	}
	else
	{
		cout << "日期非法" << endl;
	}
}

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)
		{
			_year++;
			_month = 1;
		}
	}
	return *this;
}

Date Date::operator+(int day) const
{
	//法二:复用日期 += 天数
	Date ret(*this);
	ret += day;
	return ret;
}

//日期 -=天数  d1-=100
Date& Date::operator-=(int day)
{
	//如果减去的天数是负数,要单独处理,直接调用+=运算符重载
	if (day < 0)
	{
		return *this += -day;
	}
	_day -= day;
	while (_day <= 0)
	{
		--_month;
		if (_month == 0)
		{
			_month = 12;
			--_year;
		}
		_day += GetMonthDay(_year, _month);
	}
	return *this;
}

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

//日期 - 日期
int Date::operator-(const Date& d) const
{
	int flag = 1; //方便后续计算正负
	Date max = *this;
	Date min = d;
	if (*this < d)
	{
		min = *this;
		max = d;
		flag = -1; //计算正负
	} //确保max是大的,min是小的
	int n = 0;
	while (min != max)
	{
		min++;
		n++;
	}//算出min和max之间绝对值差距
	return n * flag; //如果d1大,结果为正,d2大结果为负
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/100221.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

强化学习的基础知识和6种基本算法解释

强化学习的基础知识和概念简介&#xff08;无模型、在线学习、离线强化学习等&#xff09; 机器学习(ML)分为三个分支:监督学习、无监督学习和强化学习。 监督学习(SL):关注在给定标记训练数据的情况下获得正确的输出无监督学习(UL):关注在没有预先存在的标签的情况下发现数据…

[附源码]Python计算机毕业设计Django校园疫情防范管理系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;我…

我的周刊(第070期)

我的信息周刊&#xff0c;记录这周我看到的有价值的信息&#xff0c;主要针对计算机领域&#xff0c;内容主题极大程度被我个人喜好主导。这个项目核心目的在于记录让自己有印象的信息做一个留存以及共享。&#x1f3af; 项目streamlit[1]用 Python 快速构建数据应用&#xff1…

微信公众号开发—通过网页授权实现业务系统登录及用户绑定(微信网页授权自动登录业务系统)

&#x1f60a; 作者&#xff1a; 一恍过去&#x1f496; 主页&#xff1a; https://blog.csdn.net/zhuocailing3390&#x1f38a; 社区&#xff1a; Java技术栈交流&#x1f389; 主题&#xff1a; 微信公众号开发—通过网页授权实现业务系统登录及用户绑定(微信网页授权自…

产品经理快速入门指南之常见问题篇

产品经理作为近几年互联网最炙手可热的岗位&#xff0c;可谓是赚足了眼球。其实呢&#xff0c;产品经理是很难定义的一个角色&#xff0c;如果非要一句话定义&#xff0c;那么产品经理是为终端用户服务&#xff0c;负责产品整个生命周期的人。今天这篇文章小编会带大家了解一下…

交通部1078-2016版中的音视频协议的一点想法

交通部1078的音视频中的格式有点像TS流, 交通部中1078的音视频的解析用的走的HTTP的协议,内容是流媒体,之前用的是ffmpeg解析的内容流之前是把payload中的数据流区分出音频,视频直接塞给ffmpeg,由ffmpeg推两路流到rtmp协议上去,这种方法有点粗暴, 最近分析ffmpeg源码发现交通…

谷歌通过Chrome简化登录安卓密码支持

谷歌宣布&#xff0c;它正在为其 Chrome 网络浏览器和 Android 操作系统引入密码支持&#xff0c;以简化跨应用程序、网站和设备的登录。 谷歌今天表示&#xff1a; “密码是密码和其他可钓鱼身份验证因素的一种更安全的替代品。它们不能重复使用&#xff0c;不会在服务器漏洞…

AngularJS 2.0 稳定版真的发布了!

导读之前我们还哀叹&#xff0c;谷歌的 AngularJS 2.0 的稳定版看起来年底也未必能见到&#xff0c;然而&#xff0c;在 9 月 14 日谷歌总部召开的一个会议上&#xff0c;突然就宣布最终的稳定版发布了——而这距离前一个版本 RC7 的发布才过去了一天。 AngularJS 2.0 的开发始…

详解入门安全测试最难懂的概念 —— CSRF

对于刚刚入门安全的同学来说&#xff0c;csrf是最难理解的概念之一&#xff0c;本文会用最简单的方式对csrf进行讲解&#xff0c;包括csrf的定义&#xff0c;csrf典型的攻击流程以及如何对其进行防范&#xff0c;希望本文能够帮到大家&#xff01; CSRF定义 CSRF&#xff08;…

Nacos学习笔记 (1)Nacos的简介与安装

1. Nacos 介绍与发展前景 1.1 官网概览&#xff1a; Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service的首字母简称&#xff0c;一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。 Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一…

性能测试(一)—— 概述、策略、指标、流程

目录 一、性能测试概述 1、为什么要进行性能测试&#xff1f; 2、性能测试的概念 2.1 什么是性能&#xff1f; 2.2 什么是性能测试&#xff1f; 2.3 性能测试目的 3、性能测试与功能测试 3.1 焦点不一样 3.2 关系 二、性能测试策略 1、性能测试策略 1.1 基准测试 …

PageObject(PO)设计模式在 UI 自动化中的实践总结(以 QQ 邮箱登陆为例)

1080608 28.8 KB PO的思想最早是2013年由IT大佬Martin Flower提出的&#xff1a; martinfowler.com bliki: PageObject A page object wraps an HTML page, or fragment, with an application-specific API, allowing you to manipulate page elements for testing without d…

MVC操作方法如何绑定Stream类型的参数

1、我需要读取HTTP消息的整个 body 来填充 MVC 方法参数&#xff1b; 2、HTTP消息的 body 不是 form-data&#xff0c;而是完全的二进制内容。 最简单的方法就是不使用模型绑定&#xff0c;即在MVC方法中直接访问 HttpContext.Request.Body。 var request HttpContext.Requ…

[附源码]计算机毕业设计Python的物品交换平台(程序+源码+LW文档)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程 项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等…

使用设备树给DM9000网卡_触摸屏指定中断

目录 1 在设备树中描述网卡中断 2 dm9dev9000c.c 3 在设备树中描述触摸屏中断 1 在设备树中描述网卡中断 srom-cs420000000 {compatible "simple-bus";#address-cells <1>;#size-cells <1>;reg <0x20000000 0x8000000>;ranges;ethernet20000…

ESP 常用的低功耗配置选项解析

此篇博客介绍 ESP 常用的低功耗配置选项。 1.常用功耗优化配置选项 1.1.动态调频 CPU 工作的频率越高&#xff0c;功耗消耗也越大。通过 DFS&#xff08;dynamic frequency scaling&#xff0c;动态调频&#xff09;可以让系统自动切换工作频率&#xff0c;达到功耗和性能间…

Transformers学习笔记1. 一些基本概念和编码器、字典

Transformers学习笔记1. 一些基本概念和编码器、字典一、基本概念1. Hugging Face简介2. Transformers&#xff08;1&#xff09;简介&#xff08;1&#xff09;预定义模型&#xff08;2&#xff09;使用方法3. Datasets查看有哪些公开数据集方法1&#xff1a; 使用datasets包的…

深入分析JVM执行引擎

程序和机器沟通的桥梁 一、闲聊 相信很多朋友在出国旅游&#xff0c;或者与外国友人沟通的过程中&#xff0c;都会遇到语言不通的烦恼。这时候我们就需要掌握对应的外语或者拥有一部翻译机。而笔者只会中文&#xff0c;所以需要借助一部翻译器才能与不懂中文的外国友人交流。咱…

Android入门第51天-使用Android的SharedPreference存取信息

简介 上一篇我们介绍了在android里如何读写本地文件。我们有一种场景&#xff0c;类似网页的cookie&#xff0c;要把用户的一些储如上一次登录、使用的痕迹等信息保存下来以便于每次不需要做重复“填表单”的操作&#xff0c;当在这种场景下我们如果也使用本地文件读写的话显然…

关于Unity使用Aspose.Words创建表格单元格垂直合并不生效情况说明

文章目录&#x1f449;一、前言&#x1f449;二、问题重现1、首先看一下我用下面两段代码创建的表格&#xff1a;2、被这个问题折磨的心路历程&#x1f449;三、分析原因&#x1f449;四、解决方法&#x1f449;一、前言 最近在使用Aspose.Words.dll实现创建表格功能时&#x…