C++类与对象(4)—日期类的实现

目录

一、类的创建和方法声明

二 、输出&运算符重载

三、检查合法性

1、获取对应年月的天数

2、初始化

四、实现加等和加操作

1、先写+=再写+ 

2、先写+再写+=

3、两种方式对比

五、实现++自增和--自减

1、自增

2、自减

六、 实现减等和减操作

1、减等天数

2、加负数减正数

3、减天数

4、日期减日期

完整版:

Date.h

Date.cpp

Test.cpp


通过之前的学习,这次我们用日期类对学习过的内容进行练习,先来复习一下

 

默认成员函数如果我们不写,编译器会自动生成,我们写了,编译器就不会自动生成。

默认生成构造和析构:

  • 内置类型不做处理
  • 自定义类型会调用对应构造/析构

默认生成拷贝构造和赋值重载

  • 内置类型完成浅拷贝/值拷贝(按字节一个一个拷贝)。
  • 自定义类型,去调用这个成员拷贝构造/赋值重载 

一、类的创建和方法声明

我们在头文件中创建日期类的成员变量(属性)和成员函数(方法)的声明,定义放在源文件中。

#pragma once
#include <iostream>
#include <assert.h>
using namespace std;class Date {
public:Date(int year = 0, int month = 0, int day = 0);int GetMonthDay(int year, int month);void Print();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);// d1 - 100Date operator-(int day);// d1 - d2;int operator-(const Date& d);// ++d1Date& operator++();// d1++Date operator++(int);Date& operator--();Date operator--(int);private:int _year;int _month;int _day;
};

二 、输出&运算符重载

详细讲解请看:运算符重载

void Date::Print()
{cout << _year << "/" << _month << "/" << _day << endl;
}bool Date::operator==(const Date& d)
{return _year == d._year&& _month == d._month&& _day == d._day;
}// d1 < d2
bool Date::operator<(const Date& d)
{return _year < d._year|| (_year == d._year && _month < d._month)|| (_year == d._year && _month == d._month && _day < d._day);
}// d1 <= d2
bool Date::operator<=(const Date& d)
{return *this < d || *this == d;
}// d1 > d2
bool Date::operator>(const Date& d)
{return !(*this <= d);
}bool Date::operator>=(const Date& d)
{return !(*this < d);
}bool Date::operator!=(const Date& d)
{return !(*this == d);
}

三、检查合法性

首先要检查日期合法性,月在1-12范围内,我们通过GetMonthDay获取对应年月的天数,在用于初始化的构造函数中检查日期。

1、获取对应年月的天数

int Date::GetMonthDay(int year, int month)
{assert(month > 0 && month < 13);int _month[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400) == 0)){return 29;}else{return _month[month - 1];}
}
  • 首先通过assert判断参数月份是否合法,不合法会报错。
  • 接着创建存储对应月份天数的整型数组_month。
  • 如果传入月份为二月,同时年份为闰年,那返回29天,否则返回_month数组中对应月份的天数。

2、初始化

Date::Date(int year, int month, int day)
{if (month > 0 && month < 13&& (day > 0 && day <= GetMonthDay(year, month))){_year = year;_month = month;_day = day;}else{cout << "日期非法" << endl;}
}
  • 首先检查如果月份合法且天数合法,则进行初始化。
  • 否则输出“日期非法”提示初始化错误。

四、实现加等和加操作

1、先写+=再写+ 

日期加天数怎么算?

Date d1(2023, 2, 4);
Date d2 = d1 + 100;

思路:

  • 当前日期天数相加,大于当前月份天数,月份进位,当月份大于12月,年份进位,返回加完结果*this,但是这种方式会改变d1,所以这种方式实际上实现的是+=。
Date& Date::operator+=(int day)
{_day += day;while (_day>GetMonthDay(_year,_month)){_day -= GetMonthDay(_year,_month);month++;if(month == 13){_year++;_month = 1;}}return *this;
}

+=也需要返回值,+=支持连续+=,但日期类不支持加等类型不同,在下面的情况就+=必须要有返回值。

Date d3 = d1;
d1 = d3 += 100;

d1=d3+=100这种对日期类的操作可以支持,这时就需要加等操作的返回值了,从运算符的特性来说需要返回值。

我们通过复用加等操作实现加操作。

Date Date::operator+(int day)
{Date tmp(*this);tmp += day;return tmp;
}
  • 创建日期类对象tmp临时保存*this,
  • 对tmp进行加等操作,
  • 最后返回tmp(使用临时变量可以保证原变量不被改变,这符合+操作的定义)

另外,tmp是临时对象,出了作用域就不在了,所以不能使用传引用返回。

2、先写+再写+=

第二种方式可以先实现+操作,通过复用+操作实现+=操作。

Date Date::operator+(int day)
{Date tmp(*this);tmp._day += day;while (tmp._day > GetMonthDay(_year, _month)){tmp._day -= GetMonthDay(_year, _month);tmp._month++;if (tmp._month == 13){tmp._year++;tmp._month = 1;}}return tmp;
}
Date& Date::operator+=(int day)
{*this = *this + day;return *this;
}
  • 在+操作中借助临时变量tmp存储为变化的*this的值,对tmp的成员day进行+操作,最后返回tmp。
  • +=操作中使用*this对调用函数的对象进行+操作,最后返回*this。 

如果使用static修饰 + 操作中的tmp使其成为静态变量,变为全局生命周期,是否能使用引用返回呢?

//Date.cpp
Date& Date::operator+(int day)
{static Date tmp(*this);tmp._day += day;while (tmp._day > GetMonthDay(_year, _month)){tmp._day -= GetMonthDay(_year, _month);tmp._month++;if (tmp._month == 13){tmp._year++;tmp._month = 1;}}return tmp;
}
//test.cpp
void TestDate2()
{Date d1(2023, 2, 4);d1.Print();Date d2 = d1 + 100;d2.Print();Date d3 = d1 + 100;d3.Print();
}int main()
{TestDate2();return 0;
}

 我们可以看到d2的结果是对的,但d3的结果不对,d3应该在d1的基础上加100,但实际输出结果是在d2的基础上加100.

接下来我们来调试看一下:

当运行到 Date d3 = d1 + 100; 时,在+操作内部对tmp初始化之前,为什么tmp的成员值和*this的成员值不一样呢?

原因:静态变量只能初始化一次,第一次Date d2 = d1 + 100;使用时进行了初始化,第二次再初始化就没有效果了,所以静态变量要慎用,不能为了传引用使用static修饰tmp。

3、两种方式对比

第一种是先写加等操作,复用加等实现加操作。

第二种是先写加操作,复用加实现加等操作。

那么上述两种方式哪个好呢?

结论:第一种比较好,第二种+操作存在消耗,初始化tmp需要拷贝构造一次,传值返回还需要一次拷贝构造;而加等没有拷贝行为

如果加等复用加会增加两次拷贝构造的使用次数,先写加、加等复用加一共消耗四次拷贝次数。先写加等函数不会有拷贝构造,只有加复用加等会使用两次拷贝构造。

这样一对比就可以得出第一种先写加等再写加比较好,减少拷贝次数。

减构成运算符重载和函数重载

五、实现++自增和--自减

1、自增

自增操作符++分为前置++和后置++。

默认只有前置++,同时++只有一个操作数,那就是隐藏的this指针,返回*this的值,可以使用引用返回。

//++d
Date& Date::operator++()
{*this += 1;return *this;
}

如何区分前置和后置呢?

编译器做了特殊处理,方便识别给后置加了一个参数int,没有实际意义,只是为了构成函数重载进行区分,后置需要返回++之前的值,所以使用临时变量tmp保存*this,对*this++,然后返回tmp保存的未改变的*this。

//d++
Date Date::operator++(int)
{Date tmp(*this);*this += 1;return tmp;
}

平时中能用前置就用前置,可以减少拷贝操作。 

使用后置传个参数,任意一个即可,这个参数仅仅是为了占位,跟前置重载区分。

我们来测试一下: 

void TestDate3()
{Date d1(2023, 6, 6);d1.Print();Date ret1 = ++d1;d1.Print();ret1.Print();Date ret2 = d1++;d1.Print();ret2.Print();
}
int main()
{TestDate3();return 0;
}

输出结果: 

2、自减

同理,自减也与自增实现思路相同。

//--d
Date& Date::operator--()
{*this -= 1;return *this;
}// d-- 
Date Date::operator--(int)
{Date tmp(*this);*this -= 1;return tmp;
}

六、 实现减等和减操作

1、减等天数

在日期上做减法,如果减完小于等于零,则向上个月借日期,如果1月借日期则向上一年的12月借日期。

Date& Date::operator-=(int day)
{_day -= day;while (_day <= 0){_month--;if (_month == 0){--_year;_month = 12;}_day += GetMonthDay(_year, _month);}return *this;
}
void TestDate4()
{Date d1(2023, 2, 4);d1.Print();d1 -= 100;d1.Print();
}int main()
{TestDate4();return 0;
}

输出结果: 

 

2、加负数减正数

如果是下面这种情况呢?

void TestDate4()
{Date d2(2023, 6, 6);d2 += -100;Date d3(2023, 7, 7);d3 -= -200;
}

对加等和减等添加判断语句,如果加等负数则调用减等,减等整数负数则调用加等。

Date& Date::operator+=(int day)
{if (day < 0){*this -= -day;return *this;}_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)
{if (day < 0){*this += -day;return *this;}_day -= day;while (_day <= 0){_month--;if (_month == 0){--_year;_month = 12;}_day += GetMonthDay(_year, _month);}return *this;
}

我们再来测试一下 

void TestDate4()
{Date d2(2023, 6, 6);d2.Print();d2 += -100;d2.Print();Date d3(2023, 7, 7);d3.Print();d3 -= -200;d3.Print();
}int main()
{TestDate4();return 0;
}

 输出结果:

 

3、减天数

通过复用减等操作,同时借助临时变量tmp实现。 

Date Date::operator-(int day)
{Date tmp(*this);tmp -= day;return tmp;
}

4、日期减日期

该函数通过递增较小的日期对象,直到两个日期对象相等,计算出天数差。如果当前日期对象大于传入的日期对象,则结果为正数;如果当前日期对象小于传入的日期对象,则结果为负数。

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,分别初始化为当前日期对象和传入的日期对象 d。
  • 如果当前日期对象小于传入的日期对象 d,则交换 max 和 min,并将标志 flag 设置为 -1,表示结果为负数。
  • 创建一个整型变量 n,用于记录天数差的绝对值。
  • 使用循环,当 min 不等于 max 时,执行以下操作:递增 min 的日期,即执行 min.operator++()。同时递增 n。
  • 返回 n 乘以 flag,得到最终的天数差。

测试一下

void TestDate5()
{Date d1(2024, 1, 12);d1.Print();Date d2(2023, 11, 23);d2.Print();cout << d2 - d1 << endl;cout << d1 - d2 << endl;
}
int main()
{TestDate5();return 0;
}

成功实现 :

完整版:

Date.h

#include <iostream>
#include <assert.h>
using namespace std;class Date {
public:Date(int year = 0, int month = 0, int day = 0);void Print();int GetMonthDay(int year, int month);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);// d1 - 100Date operator-(int day);// d1 - d2;int operator-(const Date& d);// ++d1Date& operator++();// d1++Date operator++(int);Date& operator--();Date operator--(int);private:int _year;int _month;int _day;
};

Date.cpp

#include "Date.h"int Date::GetMonthDay(int year, int month)
{assert(month > 0 && month < 13);int _month[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400) == 0)){return 29;}else{return _month[month - 1];}
}Date::Date(int year, int month, int day)
{if (month > 0 && month < 13&& (day > 0 && day <= GetMonthDay(year, month))){_year = year;_month = month;_day = day;}else{cout << "日期非法" << endl;}
}Date& Date::operator+=(int day)
{if (day < 0){*this -= -day;return *this;}_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)
{Date tmp(*this);tmp += day;return tmp;
}//Date Date::operator+(int day)
//{
//	Date tmp(*this);
//	tmp._day += day;
//	while (tmp._day > GetMonthDay(_year, _month))
//	{
//		tmp._day -= GetMonthDay(_year, _month);
//		tmp._month++;
//		if (tmp._month == 13)
//		{
//			tmp._year++;
//			tmp._month = 1;
//		}
//	}
//	return tmp;
//}
//Date& Date::operator+=(int day)
//{
//	*this = *this + day;
//	return *this;
//}//++d
Date& Date::operator++()
{*this += 1;return *this;
}
//d++
Date Date::operator++(int)
{Date tmp(*this);*this += 1;return tmp;
}
//--d
Date& Date::operator--()
{*this -= 1;return *this;
}// d-- 
Date Date::operator--(int)
{Date tmp(*this);*this -= 1;return tmp;
}Date& Date::operator-=(int day)
{if (day < 0){*this += -day;return *this;}_day -= day;while (_day <= 0){_month--;if (_month == 0){--_year;_month = 12;}_day += GetMonthDay(_year, _month);}return *this;
}
Date Date::operator-(int day)
{Date tmp(*this);tmp -= day;return tmp;
}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;
}void Date::Print()
{cout << _year << "/" << _month << "/" << _day << endl;
}bool Date::operator==(const Date& d)
{return _year == d._year&& _month == d._month&& _day == d._day;
}// d1 < d2
bool Date::operator<(const Date& d)
{return _year < d._year|| (_year == d._year && _month < d._month)|| (_year == d._year && _month == d._month && _day < d._day);
}// d1 <= d2
bool Date::operator<=(const Date& d)
{return *this < d || *this == d;
}// d1 > d2
bool Date::operator>(const Date& d)
{return !(*this <= d);
}bool Date::operator>=(const Date& d)
{return !(*this < d);
}bool Date::operator!=(const Date& d)
{return !(*this == d);
}

Test.cpp

#include "Date.h"void TestDate1()
{Date d1(2023, 2, 4);cout << d1.GetMonthDay(2022,12) << endl;//d1.Print();//Date d2 = d1 + 100;//d2.Print();
}void TestDate2()
{Date d1(2023, 2, 4);d1.Print();Date d2 = d1 + 100;d2.Print();Date d3 = d1 + 100;d3.Print();
}void TestDate3()
{Date d1(2023, 6, 6);d1.Print();Date ret1 = ++d1;  // d1.operator++();d1.Print();ret1.Print();Date ret2 = d1++;  // d1.operator++(0);d1.Print();ret2.Print();/*d1.operator++();d1.operator++(0);*/
}void TestDate4()
{//Date d1(2023, 2, 4);//d1.Print();//d1 -= 100;//d1.Print();Date d2(2023, 6, 6);d2.Print();d2 += -100;d2.Print();Date d3(2023, 7, 7);d3.Print();d3 -= -200;d3.Print();
}
void TestDate5()
{Date d1(2024, 1, 12);d1.Print();Date d2(2023, 11, 23);d2.Print();cout << d2 - d1 << endl;cout << d1 - d2 << endl;
}
int main()
{TestDate5();return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【开源】基于Vue.js的数据可视化的智慧河南大屏

项目编号&#xff1a; S 059 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S059&#xff0c;文末获取源码。} 项目编号&#xff1a;S059&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块三、系统展示四、核心代码4.1 数据模块 …

Dubbo3使用Zookeeper作为注册中心的方案讨论!详解DubboAdmin与PrettyZoo来监控服务的优劣!

文章目录 一&#xff1a;Dubbo注册中心的基本使用 二&#xff1a;Zookeeper注册中心的使用 1&#xff1a;依赖引入 2&#xff1a;实际开发 三&#xff1a;Zookeeper作为注册中心的使用展示 1&#xff1a;启动注册Zookeeper服务 2&#xff1a;引入注册中心 (一)&#xf…

CTF-PWN-QEMU-前置知识

文章目录 QEMU 内存管理(QEMU 如何管理某个特定 VM 的内存)MemoryRegion gpa->hpaFlatView&#xff1a;表示MR 树对应的地址空间FlatRange&#xff1a;存储不同MR对应的地址信息AddressSpace&#xff1a;不同类型的 MemoryRegion树RAMBlock总体简化图 QEMU 设备模拟 &#x…

Redis-Redis高可用集群之水平扩展

Redis3.0以后的版本虽然有了集群功能&#xff0c;提供了比之前版本的哨兵模式更高的性能与可用性&#xff0c;但是集群的水平扩展却比较麻烦&#xff0c;今天就来带大家看看redis高可用集群如何做水平扩展&#xff0c;原始集群(见下图)由6个节点组成&#xff0c;6个节点分布在三…

基于IDEA+Tomcat+Mysql开发的企业资产管理

基于Tomcat的企业资产管理信息系统 项目介绍&#x1f481;&#x1f3fb; 企业资产管理系统”项目是一个管理和维护企业资源的平台&#xff0c;能够提供系统管理、内容管理、用户管理、组织架构维护、流程管理、模块管理、角色管理、调度管理和个人设置等功能。 本项目所述的全…

linux的基础命令

文章目录 linux的基础命令一、linux的目录结构&#xff08;一&#xff09;Linux路径的描述方式 二、Linux命令入门&#xff08;一&#xff09;Linux命令基础格式 三、ls命令&#xff08;一&#xff09;HOME目录和工作目录&#xff08;二&#xff09;ls命令的参数1.ls命令的-a选…

IDEA、PHPSTORM 在命令行中进行 PHP debug

然在终端执行控制器的方法php yii test/ab 即可看到触发debug 调试

Navicat 技术指引 | 适用于 GaussDB 的自动运行功能

Navicat Premium&#xff08;16.2.8 Windows版或以上&#xff09; 已支持对 GaussDB 主备版的管理和开发功能。它不仅具备轻松、便捷的可视化数据查看和编辑功能&#xff0c;还提供强大的高阶功能&#xff08;如模型、结构同步、协同合作、数据迁移等&#xff09;&#xff0c;这…

qt-C++笔记之终端Ctrl+C关闭界面和ROS节点

qt-C笔记之终端CtrlC关闭界面和ROS节点 code review! 文章目录 qt-C笔记之终端CtrlC关闭界面和ROS节点1.运行2.main.cpp3.main_window.hpp 1.运行 2.main.cpp 3.main_window.hpp

mysql:Error 3948 (42000): Loading local data is disabled; this must be enabled on both the client an

上面图片是AI创作&#xff0c;未经允许&#xff0c;不可商用哦&#xff01;如有更多需要&#xff0c;可私戳&#xff01; 目录 问题排查解决查看是否开启加载本地文件开启全局本地文件设置 原因分析local_infile理解mysql版本原因高版本mysql默认关闭local_infile的原因官方理解…

【JavaEE初阶】 JavaScript相应的WebAPI

文章目录 &#x1f332;WebAPI 背景知识&#x1f6a9;什么是 WebAPI&#x1f6a9;什么是 API &#x1f38d;DOM 基本概念&#x1f6a9;什么是 DOM&#x1f6a9;DOM 树 &#x1f340;获取元素&#x1f6a9;querySelector&#x1f6a9;querySelectorAll &#x1f384;事件初识&am…

逆向扒cocosjs安卓包教程-破解加密的js源码

本文只适用于cocosjs引擎打包的游戏apk,针对此类apk进行源码级别的逆向破解,可直接逐个破解工程内的源码部分,让游戏逻辑大白于你的面前,你可以针对js源码进行二次开发。按照我的教程破解过程中遇到什么问题,欢迎留言。 准备apk包 准备一个你确定用cocosjs打包好的apk包…