【C++】C++类和对象实现日期类项目------时间计算器
- 前言
- 一、项目简介
- 二、项目功能
- 三、项目实现
-
- [1. 创建头文件&源文件](#1. 创建头文件&源文件)
- [2. 定义日期类](#2. 定义日期类)
- [3. 日期+=天数](#3. 日期+=天数)
- [4. 日期-=天数](#4. 日期-=天数)
- [5. io流的函数重载](#5. io流的函数重载)
- [6. 日期 + / - 天数](#6. 日期 + / - 天数)
- [7. 前置 ++ / --](#7. 前置 ++ / --)
- [8. 后置 ++ / --](#8. 后置 ++ / --)
- [9. 比较大小](#9. 比较大小)
- [10. 日期 - 日期](#10. 日期 - 日期)
- 四、完整代码
- 结语
前言
在上一期,我们学习了C++类和对象的用法
今天,就使用类和对象来实现一个日期类的项目,来练习这部分的内容吧
(后面有完整代码,有需要可自取哦)
一、项目简介
在生活中,我们常常要计算时间
比如:计算100天以后是什么日期,计算现在离过年还有多久等等等
而这些就是一个时间计算器,它能帮助我们快速进行有关于时间的计算
下面我就放了一个时间计算器的外部链接,大家可以自行看看:
日期计算器
今天,我就来给大家讲解如何用类和对象来实现一个时间计算器
二、项目功能
想要设计一个时间计算器,首先要想好要设计出什么功能
常见的功能有:
1 . 计算一个日期+/-/+=/-=/++/--一个数、的值,其实就是将这些运算符进行重载
2 . 计算两个日期之间的差值
3 . 将两个日期进行大小比较
4 . 通过io流将日期类进行输入和输出,其实就是将<<和>>进行重载
上面我列举了超多的内容,现在我就带大家一一来实现吧!
三、项目实现
1. 创建头文件&源文件
之前在讲解扫雷游戏中我就提到:
在写复杂程序时要养成写多个头文件&源文件的好习惯,这样条理就很清晰也不会乱
详见 【C语言】扫雷游戏详解(包含递归,变色,记录时间等)

如上图:
创建了一个 " Date.h "头文件
用于存放用来放类和对象的声明和一些库函数的头文件
创建了一个 " Date.cpp "源文件
用于存放类的成员函数的定义(日期类的主体)
还有一个 " Test.cpp "源文件
用于测试实现的时间计算器运行效果
2. 定义日期类
首先我们要定义一个日期类,包含年份、月份、天份
(由于比较简单我就概括一下,若是有些看不懂的可以看看上期的类的默认成员函数希望对你有帮助)
【C++】C++------类的默认成员函数(构造、析构、拷贝构造函数)
如下,我们在头文件中定义了一个标准的类,包含类的四个默认成员函数的声明,以及成员变量的申明
代码演示:(内有注释)
(在头文件" Date.h "中写)
c
class Date
{
public:
// 全缺省的构造函数
Date(int year = 1900, int month = 1, int day = 1);
// 拷贝构造函数
Date(const Date& d);
// 赋值运算符重载
Date& operator=(const Date& d);
// 析构函数
~Date();
private:
//成员变量的申明
int _year;
int _month;
int _day;
};
声明完了肯定少不了函数的定义,函数的定义我都放在了
Date.cpp中
由于做了声明和定义的分离,所以在定义成员函数时,要使用域作用限定符::来找到Date的成员函数
代码演示:(内有注释)
(在头文件" Date.cpp "中写)
cpp
// 全缺省的构造函数
Date::Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
// 拷贝构造函数
Date::Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
// 赋值运算符重载
Date& Date::operator=(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
return *this;
}
// 析构函数
Date::~Date()
{
_year = 0;
_month = 0;
_day = 0;
}
3. 日期+=天数
当你要计算100天以后是什么日期时,这就是日期
+=天数,我们需要对运算符+=进行重载即可
(1)思路
当日期加天数时,就是变量
day加上该天数 ,此时有两种情况:
1 . 加上天数后 day 没有超出本月的天数范围
此时无需其他操作
2 . 加上天数后 day 超出本月的天数范围
此时就要进行进位处理,减去本月的天数,并将月份 +1,若月份来到了13也就是超出范围,月份就重置为 1,且年份 +1,最后再次判断day有没有超出本月的天数范围,以此循环直到符合逻辑
(2)注意
刚刚讲完了大概思路,好像就是那么回事,但是其中还是有很多大坑在等着我们呢hh
1 . 如何计算每个月份的时间?
2 . 若用户在使用的时候输入了负数咋办?
(3)代码实现
1 * 算月份天数
我们该如何计算每个月有多少天呢?而且闰年的二月又是不一样的,我们该如何处理呢?
这里可以换一种思维,直接创建一个13个数的数组表示1月到12月份的天数,并将第一个值设为0,为了方便调用,然后就直接用月份作为下标进行调用即可,最后若该月是二月且该年是闰年时,天数就+1
代码演示:(内有注释)
(在头文件" Date.cpp "中写)
这里我直接另外写一个函数来表示获取某年某月的天数
cpp
// 获取某年某月的天数
int Date::GetMonthDay(int year, int month)
{
//直接创建一个表示1月到12月天数的数组
//第一个值为0是为了方便调用
static int MonthDay[13]
= { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
//是二月且是闰年就多加一天
if (month == 2 &&
(year % 4 == 0 || year % 400 == 0) && year % 100 != 0)
{
return MonthDay[month] + 1;
}
return MonthDay[month];
}
2 * 负数处理
若用户输入
+=的天数为负数时,可以将其转换为计算-=该数的绝对值,然后直接调用-=进行处理(运算符 -= 的重载我们待会会讲解)
代码演示:(内有注释)
cpp
//若要+=的值小于0,就-=这个值的相反数
if (day < 0)
{
return *this -= (-day);
}
_day += day;
3 * 完整代码
现在我们把之前讲的思路全部串起来,如下:
代码演示:(内有注释)
(在头文件" Date.cpp "中写)
cpp
// 获取某年某月的天数
int Date::GetMonthDay(int year, int month)
{
//直接创建一个表示1月到12月天数的数组
//第一个值为0是为了方便调用
static int MonthDay[13]
= { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
//是二月且是闰年就多加一天
if (month == 2 &&
(year % 4 == 0 || year % 400 == 0) && year % 100 != 0)
{
return MonthDay[month] + 1;
}
return MonthDay[month];
}
// 日期+=天数
// 将 += 运算符进行重载
Date& Date::operator+=(int day)
{
//若要+=的值小于0,就-=这个值的相反数
if (day < 0)
{
return *this -= (-day);
}
_day += day;
//修改加后的日期,将日期进位
while(_day > GetMonthDay(_year, _month))
{
_day -= GetMonthDay(_year, _month);
_month++;
if (_month == 13)
{
_month = 1;
_year++;
}
}
return *this;
//由于是+=,改变了日期,所以传引用返回
}
4. 日期-=天数
(1)思路
当日期减天数时,就是变量
day减上该天数 ,此时有两种情况:
1 . 减上天数后 day 仍大于0
此时无需其他操作
2 . 减上天数后 day 小于0
此时就要进行退位处理,将上个月的天数补过来,并将月份 -1,若月份来到了0减没了,月份就置为 12,且年份 -1,最后再次判断day是否大于0,以此循环直到符合逻辑
(2)注意
这里的注意事项跟前面的也差不多,我就不重复介绍了,也就两点
1 . 如何计算每个月份的时间?
2 . 若用户在使用的时候输入了负数咋办?
(3)代码实现
代码演示:(内有注释)
(在头文件" Date.cpp "中写)
cpp
// 日期-=天数
// 将 -= 运算符进行重载
Date& Date::operator-=(int day)
{
//若要-=的值小于0,就+=这个值的相反数
if (day < 0)
{
return *this += (-day);
}
_day -= day;
//修改减后的日期,将日期退位
while (_day <= 0)
{
_month--;
if (_month <= 0)
{
_month = 12;
_year--;
}
//用上一个月的天数来补位
_day += GetMonthDay(_year, _month);
}
return *this;
//由于是-=,改变了日期,所以传引用返回
}
5. io流的函数重载
当我们想打印出日期的值或者输入日期的值时,只能通过用访问限定符一个一个成员变量来打印
这样就会很麻烦,那有什么简单的方法可以直接通过类似
cout<<d1<<d2<<d3 ;的方式打印?(下面我将以
cout输出为例,来给大家讲解清楚)
(1)思路
这里就可以用到
io流的函数重载,就是将<<流插入重载很简单,就直接内部调用
cout即可
(2)代码实现
但是呢,看似简单的输出,其实隐藏了许多的大坑,现在我就带大家来扫雷吧
1 * 第一步
这里我们直接根据上面的思路来写,直接在类的成员函数中调用
cout
代码演示:(内有注释)
(在头文件" Date.cpp "中写)
cpp
// <<运算符重载
// 将<<流插入进行重载
void Date::operator<<(ostream& out)
{
cout << _year << "年"
<< _month << "月"
<< _day << "日"
<< endl;
}
但是编译报错,如下图,大致意思就是参数类型不匹配

这时候我们再回头看看函数,由于 <<流插入有左右两操作数,所以在传参的时候会有一定顺序,左操作数传给左形参,而右操作数传给右形参,传错了编译器就会报错
具体解释如图:

那么该怎么改呢?
2 * 第二步
现在,为了使左右操作数参数匹配,我们可以把成员函数改为全局函数,这样就可以把隐式的Date类类型拿出来做形参,使左右操作数参数相匹配
代码演示:(内有注释)
(在头文件" Date.cpp "中写)
cpp
void operator<<(ostream& out, const Date& d)
{
cout << d._year << "年"
<< d._month << "月"
<< d._day << "日"
<< endl;
}
此时左右操作数参数相匹配,但是编译又报错,如下图,大致意思就是找不到成员变量

这是为什么呢?
当我们将成员函数改写为全局函数时,由于成员变量是Date类的私有成员,故无法在类外的函数中直接调用
3 * 第三步
要使得在类外的函数可以访问到类里的成员变量,该怎么办呢?
这时我们可以将函数变为类的友元函数,在类里面加一个友元函数声明
这样就可以访问到Date类的私有成员变量了
代码演示:(内有注释)
(在头文件 Date.h 中 Date 类中写)
cpp
//友元函数,使得全局函数可访问类中数据
friend void operator<<(ostream& out, const Date& d);
friend void operator>>(istream& in, Date& d);
这样函数就可以访问到Date类的私有成员变量了
但是,当我们只输出一个数据时,例如:
cout << d5 ;代码没问题当我们一次性输出多个数据时,例如:
cout << d5 << d6 ;代码就会报错,如下图:

这就是因为 cout 流插入的程序运行是从左往右的,最左边的 << 有左操作数 cout ,也有右操作数 d
而后面的 << 没有左操作数 cout ,只有右操作数 d,故传参不匹配,编译器报错
4 * 第四步
上面已经说了,后面的代码没有左操作数
cout,所以我们要创造一个没有左操作数cout出来
可以设立一个返回值cout,函数调用结束后,返回一个cout留在原地,来充当下一个<<的左操作数
下面我画图来进行解释:

如图,返回cout ,来充当下一个 << 的左操作数,就可以解决连续输出的问题了
代码演示:(内有注释)
(在头文件" Date.cpp "中写)
cpp
// <<运算符重载
// 将<<流插入进行重载
ostream& operator<<(ostream& out, const Date d)
{
out << d._year << "年"
<< d._month << "月"
<< d._day << "日"
<< endl;
//返回 cout 完成连续输出
return cout;
}
6. 日期 + / - 天数
若我们想知道100天以后是什么日期时,其实也是日期 + / - 天数
只是这里的计算不会改变日期本身的值,而日期+=天数会改变日期本身的值这里重载 + / - 运算符,我们直接调用刚刚的 += / -= 实现即可
代码演示:(内有注释)
(在头文件" Date.cpp "中写)
cpp
// 日期+天数
// 将 + 运算符进行重载
Date Date::operator+(int day)
{
Date tmp = *this;
tmp += day;
return tmp;
//由于是+,未改变日期,所以传值返回
}
// 日期-天数
// 将 - 运算符进行重载
Date Date::operator-(int day)
{
Date tmp = *this;
tmp -= day;
return tmp;
//由于是-,未改变日期,所以传值返回
}
7. 前置 ++ / --
前置 ++ / -- 也就是将日期推后/前一天
关于前置 ++ 和后置 ++ 的重载区别我上一期已经讲解过了,不懂的可以自行观看
【C++】C++------类的默认成员函数(构造、析构、拷贝构造函数)
前置 ++ / -- 的重载其实也很简单,只要知道前置 ++ / -- 的底层运算就行
前置 ++ / -- 是先进行 ++ / -- ,再进行运算
所以要先 ++ / -- ,再传引用返回进行运算
代码演示:(内有注释)
(在头文件" Date.cpp "中写)
cpp
// 前置++
// 将 ++ 运算符进行重载
Date& Date::operator++()
{
//前置++是先+1再运算
//故先+1再返回
*this += 1;
return *this;
}
// 前置--
// 将 -- 运算符进行重载
Date& Date::operator--()
{
//前置--是先-1再运算
//故先-1再返回
*this -= 1;
return *this;
}
8. 后置 ++ / --
后置 ++ / -- 也就是将日期推后/前一天
后置 ++ / -- 的重载是先进行运算,再进行 ++ / --
所以要先拷贝先前的值,再进行 ++ / --,最后传值返回进行运算
代码演示:(内有注释)
(在头文件" Date.cpp "中写)
cpp
// 后置++
// 将 ++ 运算符进行重载
Date Date::operator++(int)
{
//后置++是先运算再+1
//故先拷贝以前的值返回再+1
Date tmp = *this;
*this += 1;
return tmp;
}
// 后置--
// 将 -- 运算符进行重载
Date Date::operator--(int)
{
//后置--是先运算再-1
//故先拷贝以前的值返回再-1
Date tmp = *this;
*this -= 1;
return tmp;
}
9. 比较大小
这个比较简单,只需要年和年比,年相等就月和月比,月相等就天和天比,直到比出大小
这里我就列举一个 > 运算符重载的比较,后面的 < 、>= 、 <= 、== 、!= 等就大家自行解决啦
(最后也有完整代码)
代码演示:(内有注释)
(在头文件" Date.cpp "中写)
cpp
// >运算符重载
// 将 > 运算符进行重载
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;
}
}
10. 日期 - 日期
当我们在计算今天距离放暑假还有多少天时,就是两日期的相减,故需要我们对
-运算符进行重载
这里我分为两步:
1 . 先通过比较大小找出较大的那个日期
2 . 不断用小日期 +1 直到与大日期相等,最后返回总共相加的值即可
代码演示:(内有注释)
(在头文件" Date.cpp "中写)
cpp
// 日期-日期 返回天数
// 将 - 运算符进行重载
int Date::operator-(const Date& d)
{
int n = 0;
//找出小日期和大日期
Date min = *this;
Date max = d;
if (min > max)
{
min = d;
max = *this;
}
//直接不断用小日期+1,直到与大日期相等
//最后返回相加的值
while (min != max)
{
min++;
n++;
}
return n;
}
四、完整代码
在Date.h头文件写到:
(用于存放用来放类和对象的声明和一些库函数的头文件)
cpp
#pragma once
#include<iostream>
using namespace std;
class Date
{
//友元函数,使得全局函数可访问类中数据
friend ostream& operator<<(ostream& out, const Date d);
friend istream& operator>>(istream& in, Date& d);
public:
// 打印
void DatePrint();
// 获取某年某月的天数
int GetMonthDay(int year, int month);
// 全缺省的构造函数
Date(int year = 1900, int month = 1, int day = 1);
// 拷贝构造函数
Date(const Date& d);
// 赋值运算符重载
Date& operator=(const Date& d);
// 析构函数
~Date();
// 日期+=天数
// 将 += 运算符进行重载
Date& operator+=(int day);
// 日期+天数
// 将 + 运算符进行重载
Date operator+(int day);
// 日期-天数
// 将 - 运算符进行重载
Date operator-(int day);
// 日期-=天数
// 将 += 运算符进行重载
Date& operator-=(int day);
// 前置++
// 将 ++ 运算符进行重载
Date& operator++();
// 后置++
// 将 ++ 运算符进行重载
Date operator++(int);
// 后置--
// 将 -- 运算符进行重载
Date operator--(int);
// 前置--
// 将 -- 运算符进行重载
Date& operator--();
// >运算符重载
// 将 > 运算符进行重载
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);
// 日期-日期 返回天数
// 将 - 运算符进行重载
int operator-(const Date& d);
private:
//成员变量的申明
int _year;
int _month;
int _day;
};
在Date.cpp源文件中写到:
(用于存放类的成员函数的定义(日期类的主体))
cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include"Date.h"
// 打印
void Date::DatePrint()
{
//加 - 方便观察
cout << _year << "-" << _month << "-" << _day << endl;
}
// 获取某年某月的天数
int Date::GetMonthDay(int year, int month)
{
//直接创建一个表示1月到12月天数的数组
//第一个值为0是为了方便调用
static int MonthDay[13]
= { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
//是二月且是闰年就多加一天
if (month == 2 &&
(year % 4 == 0 || year % 400 == 0) && year % 100 != 0)
{
return MonthDay[month] + 1;
}
return MonthDay[month];
}
// 全缺省的构造函数
Date::Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
// 拷贝构造函数
Date::Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
// 赋值运算符重载
Date& Date::operator=(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
return *this;
}
// 析构函数
Date::~Date()
{
_year = 0;
_month = 0;
_day = 0;
}
// 日期+=天数
// 将 += 运算符进行重载
Date& Date::operator+=(int day)
{
//若要+=的值小于0,就-=这个值的相反数
if (day < 0)
{
return *this -= (-day);
}
_day += day;
//修改加后的日期,将日期进位
while(_day > GetMonthDay(_year, _month))
{
_day -= GetMonthDay(_year, _month);
_month++;
if (_month == 13)
{
_month = 1;
_year++;
}
}
return *this;
//由于是+=,改变了日期,所以传引用返回
}
// 日期+天数
// 将 + 运算符进行重载
Date Date::operator+(int day)
{
Date tmp = *this;
tmp += day;
return tmp;
//由于是+,未改变日期,所以传值返回
}
// 日期-天数
// 将 - 运算符进行重载
Date Date::operator-(int day)
{
Date tmp = *this;
tmp -= day;
return tmp;
//由于是-,未改变日期,所以传值返回
}
// 日期-=天数
// 将 -= 运算符进行重载
Date& Date::operator-=(int day)
{
//若要-=的值小于0,就+=这个值的相反数
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++()
{
//前置++是先+1再运算
//故先+1再返回
*this += 1;
return *this;
}
// 后置++
// 将 ++ 运算符进行重载
Date Date::operator++(int)
{
//后置++是先运算再+1
//故先拷贝以前的值返回再+1
Date tmp = *this;
*this += 1;
return tmp;
}
// 后置--
// 将 -- 运算符进行重载
Date Date::operator--(int)
{
//后置--是先运算再-1
//故先拷贝以前的值返回再-1
Date tmp = *this;
*this -= 1;
return tmp;
}
// 前置--
// 将 -- 运算符进行重载
Date& Date::operator--()
{
//前置--是先-1再运算
//故先-1再返回
*this -= 1;
return *this;
}
// >运算符重载
// 将 > 运算符进行重载
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 _year == d._year &&
_month == d._month &&
_day == d._day;
}
// >=运算符重载
// 将 >= 运算符进行重载
bool Date::operator >= (const Date& d)
{
//直接用刚刚写的==和>
return *this == d || *this > d;
}
// <运算符重载
// 将 < 运算符进行重载
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);
}
// 日期-日期 返回天数
// 将 - 运算符进行重载
int Date::operator-(const Date& d)
{
int n = 0;
//找出小日期和大日期
Date min = *this;
Date max = d;
if (min > max)
{
min = d;
max = *this;
}
//直接不断用小日期+1,直到与大日期相等
//最后返回相加的值
while (min != max)
{
min++;
n++;
}
return n;
}
// <<运算符重载
// 将<<流插入进行重载
ostream& operator<<(ostream& out, const Date d)
{
out << d._year << "年"
<< d._month << "月"
<< d._day << "日"
<< endl;
//返回 cout 完成连续输出
return cout;
}
// >>运算符重载
// 将>>流提取进行重载
istream& operator>>(istream& in, Date& d)
{
in >> d._year >> d._month >> d._day;
//返回 cin 完成连续输入
return cin;
}
在Test.cpp源文件中写到:
(用于测试实现的时间计算器运行效果)
(这里是我的一些测试数据)
cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include"Date.h"
int main()
{
Date d1, d3;
Date d2(2007, 10, 23);
d3 = d2;
Date d4 = d2;
//Date d4(d2);
d1.DatePrint();
d2.DatePrint();
d3.DatePrint();
d4.DatePrint();
d3 += 1000;
d4 = d4 + 1000;
d3.DatePrint();
d4.DatePrint();
d3 -= 1000;
d4 = d4 - 1000;
d3.DatePrint();
d4.DatePrint();
cout << d3 - d1 << endl;
//重载<<、>>
Date d5, d6;
cin >> d5 >> d6;
cout << d5 ;
cout << d5 << d6;
return 0;
}
结语
本期资料来自于:

OK,本期的时间计算器实现到这里就结束了
若内容对大家有所帮助,可以收藏慢慢看,感谢大家支持
本文有若有不足之处,希望各位兄弟们能给出宝贵的意见。谢谢大家!!!
新人,本期制作不易希望各位兄弟们能动动小手,三连走一走!!!
支持一下(三连必回QwQ)