目录

一、前言

二、日期类的实现

检查日期的合法性

<  运算符重载

== 运算符重载

<= 运算符重载

>  运算符重载

>= 运算符重载

!= 运算符重载

改进和优化

日期 + 天数

日期 += 天数

日期 -= 天数

日期 - 天数

前置 ++

后置 ++

前置 --

后置 --

日期 - 日期

三、总结


一、前言

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

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

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

二、日期类的实现

检查日期的合法性

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

class Date
{
public:bool isLeapYear(int year) //判断是否为闰年{//四年一闰百年不闰或四百年一闰return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);}int GetMonthDay(int year, int month){//加上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天elsereturn 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;elsereturn 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;elsereturn 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;}elsecout << "日期非法" << 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;elsereturn 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去初始化retret._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时单独讨论。具体步骤如下:

  1. 当减的天数为负数,则为+=,直接调用
  2. 若减后的day<0,月-1
  3. 若月 = 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++ 】日期计算器相关推荐

  1. 【C++】日期类+日期万年历+日期计算器

    对于日期类,我们主要实现一下日期类的基本函数,构造,拷贝构造,运算符的重载,析构.当然这里运算符的重载需要实现的还是挺多的,如:=.<.>.<=.>=.等 #include & ...

  2. javascript 日期计算器 推算几天后的日期 计算日期差

    <html> <head> <meta http-equiv="Content-Type" content="text/html; char ...

  3. c语言日期加减天数,日期计算器

    一  完成的功能 注意:此日历只能用于计算1982年10月15日以及以后的日期 1.日期+/-天数=返回日期(处理:1.如果+/-一个负的天数的情况 2.如果加之后,或减之后的日期非法) 2.两个日期 ...

  4. 【C++】---日期计算器

    [C++] 日期计算器 注:可能有时你也会被这样的问题所烦恼,你想要知道自己活了多少天的话,乍一想这该怎么计算呢,捋一捋,要计算平年闰年,每个月多少天等等等苦恼着,下面我们通过C++来实现一个日期计算 ...

  5. 日期计算器-java(含界面)

    日期计算器 一. 系统描述 完成日期计算器的编写. 系统功能: 1.输入日期,完成星期几的计算,并输出星期值 2.输入日期加天数,完成日期加/减天数的计算,并输出新日期 3.输入日期以及一个年月日,完 ...

  6. python日期计算器 青少年编程电子学会python编程等级考试二级真题解析2021年12月

    目录 python日期计算器 一.题目要求 1.编程实现 2.输入输出 3.评分标准

  7. c++Date(日期)类方法实现日期计算器

    1.日期类应该具有什么功能 计算两个日期间的间隔时间 给定一个日期,计算出减少x天数后的日期 给定一个日期,减少x天数后的日期 2.日期类的具体实现 需要注意的是: 为了提高的安全性,我只将要实现对象 ...

  8. Python用Tkinter实现日期计算器

    废话不多说,直接上代码 import tkinter import datetime from tkinter import * from tkinter.ttk import * from tkin ...

  9. 用C语言写一个日期计算器

    首先,我们应知道日期计算器包括哪些功能 1.明天的日期 2 .n天后的日期 3.两个日期之间的天数 我们先从第一个功能开始,首先创建一个日期的结构体,包括:年.月.日. struct date {in ...

  10. 【C++】日期计算器

    文章目录 一.前言 二.日期类的实现 1.Date类中默认成员函数的使用 1.构造函数 2.析构函数 3.拷贝构造函数 4.赋值运算符重载 5.const成员函数 6.取地址操作符重载和const取地 ...

最新文章

  1. RTSP流媒体数据传输的两种方式(TCP和UDP)
  2. nofollow标签_如何Nofollow外链
  3. 非计算机专业的学生,简谈非计算机专业的计算机教育
  4. Linux getcwd()的实现【转】
  5. eclipse插件安装的方法
  6. Spring Boot 自动配置原理
  7. Coursera自动驾驶课程第9讲:Visual Features Detection Description and Matching
  8. scrapy框架_Python学习之Scrapy框架
  9. [LibTorch Win] 各版本 LibTorch 下载
  10. noapic和acpi=off能帮我做到什么?
  11. 计算机操作系统核心知识点总结面试笔试要点
  12. Web前端开发的项目开发流程
  13. 【LeetCode】马三来刷题之Remove Duplicates from Sorted Array
  14. VUE 对@click的认识
  15. 如何在手机下载微博APP中视频?【最新有效方法】
  16. 超级详细-NMOS、PMOS的工作原理及相关内容整理(下)
  17. 服务器w8系统如何重装,华硕w8系统重装图文教程
  18. 控制器中接收数据的四种方式
  19. Linux中的stdout和stderr
  20. CISSP-D8-软件开发安全

热门文章

  1. TypeScript学习--Symbols
  2. 安卓手机刷linux超频内核,内核超频教程
  3. 各种字体.效果和名字对应图. --- by 王朋
  4. 四种方法解决微信小程序设置背景图片,哪种最好?
  5. 精心分享8个让人大开眼界的软件和网站,极大提高办公效率
  6. radio按扭设置只读_关于html:为什么单选按钮不能为“只读”?
  7. Chrome浏览器常用快捷键总结
  8. EntityRef:expecting“;”
  9. 5分钟使用Python爬取豆瓣TOP250电影榜
  10. Pygame实战:花巨资筹备的一款Tom猫游戏,你玩过嘛?