基于C++实现日期计算器的详细教程

目录
  • 一、前言
  • 二、日期类的实现
    • 检查日期的合法性
    • <  运算符重载
    • == 运算符重载
    • <= 运算符重载
    • >  运算符重载
    • >= 运算符重载
    • != 运算符重载
    • 改进和优化
    • 日期 + 天数
  • 日期 += 天数
    • 日期 -= 天数
    • 日期 - 天数
    • 前置 ++
    • 后置 ++
    • 前置 --
    • 后置 --
    • 日期 - 日期
  • 三、总结

一、前言

在我们的日常生活中,我们可能需要计算几天后的日期,或计算日期差等,现如今计算日期的方式有很多,简单粗暴的直接查看日历,快捷点的直接使用日期计算器来求得,先给一个网络上的日期计算器截图:

现在,就让我们用代码来实现其工作原理吧。

注意:本篇日期类.h文件放声明,.cpp文件放定义

二、日期类的实现

检查日期的合法性

实现日期类首先就得检查日期的合法性,这其中就包括大小月,闰年的2月有29天,一年只有12个月等等细节都要考虑到。

class Date
{
public:
    bool isLeapYear(int year) //判断是否为闰年
    {
    	//四年一闰百年不闰或四百年一闰
    	return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
    }
	static int GetMonthDay(int year, int month)
//加上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的天

这三种全是小于的情况,返回true,其余返回false

代码如下:

// <运算符重载
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);
}

改进和优化

上述我们写的运算符重载都是建立在声明定义分离的,这里我们可以对其进行优化,如下:

先前我们学过内联,可以帮助我们对于短小函数减少函数调用而引发的效率损失问题,因此我们可以把上述几个运算符重载函数放成内联,此外,有一种简单粗暴的方法:直接在类里定义,因为类里的函数默认内联,还省的我们自己写inline,而且我们也不用在类外加上类域了,当然,有些长的函数还是声明和定义分离比较好。

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);
		//return (d < *this);
	}
	// >=运算符重载
	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;
};

Date.cpp 文件:

#include"Date.h"
//获取某月天数
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 <= GetMonthDay(year, month))
	{
		_year = year;
		_month = month;
		_day = day;
	}
	else
		cout << "日期非法" << endl;
}
// <运算符重载
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;
}

日期 + 天数

思路:

对于日期 + 天数,我们得到的还是一个日期。特别需要注意进位的问题(天满了往月进,月满了往年进),主要考虑如下几个特殊点:

加过的天数超过该月的最大天数,需要进位

当月进位到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上做改动只会导致原有的日期也变化,而这个变化正是我日期 += 天数的需求

仔细想想:+=天数就是在原有的日期上再加一定的天数,直接对*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; //让d1+过天数后再返回给自己从而实现+=
	return *this;
}

法一和法二熟优?

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

日期 -= 天数

思路:

日期-=天数得到的还是一个日期,且是在原日期的基础上做改动。合法的日期减去天数后的day只要>0就没问题,若小于0就要借位了。要注意当减去的天数<0时单独讨论。具体步骤如下:

  • 当减的天数为负数,则为+=,直接调用
  • 若减后的day<0,月-1
  • 若月 = 0,则年-1,月置为12

代码如下:

//日期 -=天数  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;
}

前置 ++

思路:

C++里有前置++和后置++,这就导致一个巨大的问题,该如何区分它们,具体实现过程不难(直接复用+=即可),难的是如何区分前置和后置。因此C++规定,无参的为前置,有参的为后置。

代码如下:

//前置++
Date& Date::operator++() //无参的为前置
{
	*this += 1; //直接复用+=
	return *this;
}

后置 ++

思路:

有参的即为后置,后置++拿到的返回值应该是自己本身未加过的,因此要先把自己保存起来,再++*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大结果为负
}

三、总结

本篇日期类把先前学到的引用,传值/传引用返回、拷贝构造、复用等等知识点柔和到了一起,非常值得大家操手练习练习,创作不易,还望三连。

日期类的源码链接:gitee链接:日期计算器完善版

到此这篇关于基于C++实现日期计算器的文章就介绍到这了,更多相关C++日期计算器内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C/C++实现日期计算器的示例代码

    问题介绍: 今天突然看到一个问题看起来蛮有趣的,跟大家分享一下. 给定任意日期对该日期进行加减天数,最后得出加减后出现的日期.以及给两个日期你可以得出他们两个之间相隔多少天.(需要考虑闰年,每个月天数不同,我们需要写一个我们直接可以使用的日期加减器)因为时间比较仓促,我也没有写界面,只有其中几个主要的函数的架构思想以及简单的调试就发出来了. 代码实现: #include<iostream> #include<Windows.h> #include<assert.h> u

  • 基于C++实现简单日期计算器

    基于C++编写的简单的日期计算器,供大家参考,具体内容如下 以下代码是在VS2013下编译运行的: 下面展示我的测试结果: 标题 代码如下: #include<iostream> using namespace std; #include"vld.h"//这个是检测虚拟内存是否泄露,我在这里安装了vld,所以直接用,包含头文件就好了 class Date { public: //构造函数 Date(int year=2018, int month=8, int day=16

  • 基于C++实现日期计算器的详细教程

    目录 一.前言 二.日期类的实现 检查日期的合法性 <  运算符重载 == 运算符重载 <= 运算符重载 >  运算符重载 >= 运算符重载 != 运算符重载 改进和优化 日期 + 天数 日期 += 天数 日期 -= 天数 日期 - 天数 前置 ++ 后置 ++ 前置 -- 后置 -- 日期 - 日期 三.总结 一.前言 在我们的日常生活中,我们可能需要计算几天后的日期,或计算日期差等,现如今计算日期的方式有很多,简单粗暴的直接查看日历,快捷点的直接使用日期计算器来求得,先给一个网

  • 基于CentOS的OpenStack环境部署详细教程(OpenStack安装)

    效果展示: 环境准备controller节点: 6GB 4H 60GB/30GB/30GB compute节点 6GB 4H 60G/60G admin节点(ceph admin管理节点) 2GB 4H 60GB/6GB OpenStack简介 OpenStack是一个开源的云平台管理项目,可以用于构建公有云或私有云平台,提供了基础设施及服务(Iaas)的解决方案,OpenStack是一个云操作系统,通过数据中心可控制计算.存储.网络等资源池,OpenStack覆盖了网络.虚拟化.操作系统.服务

  • 基于SVN源码服务器搭建(详细教程分析)

    一.引言笔者曾经试图在网上搜索一篇关于SVN源代码服务器搭建方面的中文技术文章,可惜,所找到的,要么是不完整,要么就是对笔者没什么帮助的文章,TortoiseSvn的帮助文档固然强大,但因为是英文,不是很适合来作为入门者使用:毕竟,TortoiseSvn是一个客户端,服务器端的配置也很重要.一年前,笔者刚刚毕业,初入公司,在进行开发时,由于没有进行软件代码版本管理,导致的结果是•软件一天一个版本•Bug日益增加且隐藏很深•代码无法向前回溯•几个月前修正过的Bug在几个月后又重新出现由于最近公司决

  • 基于docker部署Jenkins的详细教程

    0.我做这个文档的时候,大概是19年12月前后,那会er2.200是最新版的. 1.下载镜像 docker pull jenkins/jenkins 一定要安装最新版的 jenkins,旧版本,很多插件无法安装 2.查看jenkins的详细信息 docker inspect jenkins:2.200 3.创建jenkins 目录(做持久化) mkdir /home/jenkins_home 4.启动容器 docker run -d --name Jenkins_01 -p 8081:8080

  • 基于PHP实现用户登录注册功能的详细教程

    教程前先给大家看看小编的实现成果吧! 图1: 图2: 图3: 教程: 实现这个功能我们需要五个php文件: login.php (登录界面,如图2) <!DOCTYPE html> <html><head> <title>登录</title> <meta name="content-type"; charset="UTF-8"> </head><body> <div

  • 基于C++实现一个日期计算器

    目录 一.日期计算器的功能 二.获取每个月的天数 三.Date类中的默认成员函数 1.构造函数 2.析构函数 3.拷贝构造 4.赋值运算符重载 四.运算符重载 1.+=.+.-=.- 2.==.!=.>.>=.<.<= 3.前置++和--.后置++和-- 五.日期类代码 一.日期计算器的功能 实现日期类的==.!=.+=.+.-=.-.>=.>.<=.<.前置++和--.后置++和--. 二.获取每个月的天数 int GetMonthDay(int year

  • 基于CentOS8系统使用Docker搭建Gitlab的详细教程

    目录 一.安装Docker 二.安装GitLab 三.初始化GitLab 一.安装Docker #1.卸载旧版本 yum remove docker \ docker-client \ docker-client-latest \ docker-common \ docker-latest \ docker-latest-logrotate \ docker-logrotate \ docker-engine #2.下载需要的安装包 sudo yum install -y yum-utils #

  • 使用vue.js2.0 + ElementUI开发后台管理系统详细教程(二)

    在上篇文章给大家介绍了使用vue.js2.0 + ElementUI开发后台管理系统详细教程(一) 1. 引入路由工具vue-router,切换视图 # 安装vue-router cnpm install vue-router --save-dev 2. 使用vue-router main.js import Vue from 'vue' import App from './App' import VueRouter from 'vue-router' import routeConfig f

  • 使用vue.js2.0 + ElementUI开发后台管理系统详细教程(一)

    1. 根据官方指引,构建项目框架 # 安装vue $ cnpm install vue@2.1.6 # 全局安装 vue-cli $ cnpm install --global vue-cli # 创建一个基于 webpack 模板的新项目my-project $ vue init webpack my-project # 进入项目目录 $ cd my-project # 安装依赖,走你 $ cnpm install # 运行项目 $ cnpm run dev 2. 运行项目之后,会看到以下界面

随机推荐