【c++】——类和对象(中)——实现完整的日期类

news2024/11/16 21:39:48

作者:chlorine

专栏:c++专栏

我的花一定会开。

【学习目标】

  • 拷贝复制——赋值运算符重载

目录

🎓运算符重载(-><=...)

🎓日期&天数

🎓前置++和后置++重载


我们完成了赋值运算符重载章节的学习,对operator关键字的使用有了一定的了解,接下来我们要来实现相对完整的日期类

#include<iostream>
using namespace std;
class Date
{
public:
	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	Date(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

	bool operator<(const Date& d)
	{
		if (_year < d._year)
		{
			return true;
		}
		else if (_year == d._year && _month < d._month)
		{
			return true;
		}
		else if (_year == d._year && _month == d._month && _day < d._day)
		{
			return true;
		}
		else
		{
			return false;
		}
	}

	void print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}

private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1(2023, 10, 5);
	Date d2(2023, 11, 5);
	int ret=d1<d2;
	cout << ret << endl;
	Date d3(d1);
	d1.print();
	d2.print();
	d3.print();
	return 0;
}

我们要对以上的代码进行声明定义分离。

拷贝构造需要写嘛?赋值重载需要写嘛?析构需要写嘛?——都不需要

唯一要写的就是——构造!

让我们回顾一下类的俩种定义方式:

  • 1. 声明和定义全部放在类体中,需注意:成员函数如果 在类中定义 ,编译器可能会将其当成 内联函数 处理。
  • 类声明放在 .h 文件中,成员函数定义放在 .cpp 文件中,注意: 成员函数名前需要加类名 ::

声明和定义分离,不能同时(声明和定义)都给构造函数缺省参数。

注意:成员函数名前需要加类名::

所以我们对于构造函数的声明定义,应以下形式:

好嘞,接下来真正的来实现了。

🎓运算符重载(-><=...)

对于我们d1的日期大于另一个日期,就返回true,但是如果小于呢,如果小于等于,等于,大于等于呢.....我们每次都要改下面一段代码嘛?未必也太麻烦了吧

bool Date::operator<(const Date& d)
{
	if (_year < d._year)
	{
		return true;
	}
	else if (_year == d._year && _month < d._month)
	{
		return true;
	}
	else if (_year == d._year && _month == d._month && _day < d._day)
	{
		return true;
	}
	else
	{
		return false;
	}
}
bool Date::operator==(const Date& d)
{
	return _year == d._year
		&& _month ==d._month
		&& _day == d._day;
}

我们首先写了 (< 和 ==)运算符的运算符重载函数,接下来我们要对 (> >= <= )进行操作。因为==和<可以完成后续的操作符的函数写照。

  • 首先我们看看<=

我们先可以形成 d1<=d2

//d1<=d2
bool Date::operator<=(const Date& d)

d1是this指针,d2是d,所以我们就可以想到,<=不就是 <和=的结合嘛?

//d1<=d2
bool Date::operator<=(const Date& d)
{
	return *this < d || *this == d;
}

 

那么接下来的 >= 和 > 不就由任而解了嘛~

那我就将完整代码给你们看看。

//.h文件中声明
    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);
  //.cpp文件中定义
bool Date::operator<(const Date& d)
{
	if (_year < d._year)
	{
		return true;
	}
	else if (_year == d._year && _month < d._month)
	{
		return true;
	}
	else if (_year == d._year && _month == d._month && _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)
{
	return !(*this <= d);
}

bool Date::operator>=(const Date& d)
{
	return !(*this < d);
}

bool Date::operator!=(const Date& d)
{
	return !(*this == d);
}

完美的进行了一波复用。这个代码不仅支持日期类,也支持任意类型的复用。

重载一个<和=,或者重载一个>和=,都是可以用复用,因为它们本身都存在互斥关系。

其实这里我们讲述一个通用的方法,以后会有更多类型使用复用。

🎓日期&天数

日期类还有另一种方式的。

日期+日期 显然没有意义的,有日期-日期有意义,日期+-天数也是有意义的。接下来我们实现这段函数重载。

如果我想算一下从今天2023/11/14,那么一百天是哪一天?

我们首先要知道,每一个月份的天数是不一样的,特别是2月得考虑平年闰年,闰年29天,平年28天,闰年的判断规则是(四年一润,百年不润,再400年润)挺复杂的,获取每个月的天数,首先我们得完成。

int Date::GetMonthDay(int year, int month)
{
	int daysArr[13] = { 0,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 daysArr[month];
	}
}

那我们接下来就写一下下面函数

//d+100
	Date& operator+(int day);

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;
}

直到_day小于当月的天数,就给月份+1,如果月份=13,就给年份+1.

结果是2024/2/22

但是这样写的话,是不是给d1本身改变了?如何不让其本身改变呢?就像i=10,i+100中的i会不会变?——不会变,因为会有返回值,但是这里的d1改变了。其实这里我们对d1+100,严格上完成的是+=,+=有返回值。所以不改变原来的值。

+不能改变自己

  • 如何用+来进行保存d1的原本的值呢?——临时变量tmp
  • 如何利用临时变量呢?——拷贝构造

大家有没有发现这里是个大杂烩,前面的知识进行融会贯通,这里就考察了拷贝构造,d1+100,d1是this,那么就将this拷贝给tmp,然后将tmp+day,返回tmp临时变量,不用引用返回,出了函数就销毁了。

//d1+day
Date Date::operator+(int day)
{
	Date tmp(*this);
	tmp._day += day;
	while (tmp._day > GetMonthDay(tmp._year,tmp. _month))
	{
		tmp._day -= GetMonthDay(tmp._year, tmp._month);
		++tmp._month;
		if (tmp._month == 13)
		{
			++tmp._year;
			tmp._month = 1;
		}
	}
	return tmp;
}

这就实现了用+来保留d1的原有值,不改变它的值,创建临时变量进行拷贝构造,就可以用+来实现。这里我还要增加一个点,这里+=和+我们有没有联想到上面的复合运用?如果用+=复合+,又如何用+复合+=?

//+=复用+
Date& Date::operator+=(int day)
{
	*this = *this + day;
	return *this;
}
Date Date::operator+(int day)
{
	Date tmp(*this);
	tmp._day += day;
	while (tmp._day > GetMonthDay(tmp._year,tmp. _month))
	{
		tmp._day -= GetMonthDay(tmp._year, tmp._month);
		++tmp._month;
		if (tmp._month == 13)
		{
			++tmp._year;
			tmp._month = 1;
		}
	}
	return tmp;
}
//+复用+=
Date Date::operator+(int day)
{
	Date tmp(*this);
	tmp += day;
	return tmp;
}

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;
}

按上面的代码,更推荐的是第二种。


🎓前置++和后置++重载

  • 前置++:返回++之后的对象(默认前置++,参数增加int)
  • 后置++:返回++之前的对象

这样就可以吗?运算符重载和函数重载都用了重载的词,但是不一样的,运算符重载是自定义类型,函数重载是函数名相同参数不同。这里是无法进行函数重载的,因为函数名相同参数也相同。为了让它们重载呢?我们就在后置++增加一个int参数,int参数并不是接收具体的值,仅仅是占位。

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

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

以示区分。前置++,后置++。内置类型的前置后置++效率没有什么问题,自定义类型的前置后置++有区别,看上面前置和后置的代码,而且编译器自动默认前置,而且后置创建了临时变量。

前置++和后置++都是一元运算符,为了让前置++与后置++形成能正确重载

  • 前置++:返回+1之后的结果  注意:this指向的对象函数结束后不会销毁,故以引用方式返回提高效率。
  • C++规定:后置++重载时多增加一个int类型的参数,但调用函数时该参数不用传递,编译器自动传 递  
  • 后置++:注意:后置++是先使用后+1,因此需要返回+1之前的旧值,故需在实现时需要先将this保存一份, 然后给this+1   而temp是临时对象,因此只能以值的方式返回,不能返回引用。

我的花一定会开的。

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

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

相关文章

wpf devexpress项目中添加GridControl绑定数据

本教程讲解了如何添加GridControl到wpf项目中并且绑定数据 原文地址Lesson 1 - Add a GridControl to a Project and Bind it to Data | WPF Controls | DevExpress Documentation 1、使用 DevExpress Template Gallery创建一个新的空白mvvm应用程序&#xff0c;这个项目包括了…

算法笔记-第七章-队列

算法笔记-第七章-队列 队列的相关知识点c中队列queue用法队列的操作序列求和队列约瑟夫环-队列匹配队列 队列的相关知识点 大佬的讲解 c中队列queue用法 一&#xff1a;queue是一种容器转换器模板&#xff0c;调用#include< queue>即可使用队列类 二&#xff1a;使用q…

Python--集合----无序,去重,空集合只能用set()方法

集合&#xff08;set&#xff09;是一个无序的不重复元素序列。 特点&#xff1a;天生去重 无序 集合定义&#xff1a;在Python中&#xff0c;我们可以使用一对花括号 {} 或者 set()方法 来定义集合&#xff0c; 但是如果你 定义的集合是一个 空集合&#xf…

SpringCloud Alibaba(上):注册中心-nacos、负载均衡-ribbon、远程调用-feign

Nacos 概念&#xff1a;Nacos是阿里巴巴推出的一款新开源项目&#xff0c;它是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。Nacos致力于帮助用户发现、配置和管理微服务&#xff0c;它提供了一组简单易用的特性集&#xff0c;包括动态服务发现、服务配置…

线性代数本质系列(二)矩阵乘法与复合线性变换,行列式,三维空间线性变换

本系列文章将从下面不同角度解析线性代数的本质&#xff0c;本文是本系列第二篇 向量究竟是什么&#xff1f; 向量的线性组合&#xff0c;基与线性相关 矩阵与线性相关 矩阵乘法与复合线性变换 三维空间中的线性变换 行列式 逆矩阵&#xff0c;列空间&#xff0c;秩与零空间 克…

P6入门:项目初始化9-项目详情之资源 Resource

前言 使用项目详细信息查看和编辑有关所选项目的详细信息&#xff0c;在项目创建完成后&#xff0c;初始化项目是一项非常重要的工作&#xff0c;涉及需要设置的内容包括项目名&#xff0c;ID,责任人&#xff0c;日历&#xff0c;预算&#xff0c;资金&#xff0c;分类码等等&…

HTTP服务器——tomcat的安装和使用

文章目录 前言下载tomcattomcat 文件bin 文件夹conf 文件lib 文件log 文件temp 文件webapps 文件work 目录 如何使用 tomcat 前言 前面我们已经学习了应用层协议 HTTP 协议和 HTTP 的改进版——HTTPS&#xff0c;这些协议是我们在写与服务器相关的代码的时候息息相关的&#x…

cocosCreator 之内存管理和释放

版本&#xff1a; 3.4.0 语言&#xff1a; TypeScript 环境&#xff1a; Mac 回顾 前面有两篇博客说明了&#xff1a; cocosCreator 之 resources动态加载、预加载 讲述了静态引用资源&#xff0c;动态加载和预加载相关cocosCreator 之 Bundle 讲述了AssetManager关于对内置…

将随机数设成3407,让你的深度学习模型再涨一个点!文再附3种随机数设定方法

随机数重要性 深度学习已经在计算机视觉领域取得了巨大的成功&#xff0c;但我们是否曾想过为什么同样的模型在不同的训练过程中会有不同的表现&#xff1f;为什么使用同样的代码&#xff0c;就是和别人得到的结果不一样&#xff1f;怎么样才能保证自己每次跑同一个实验得到的…

技术管理责任制度《一》

一、技术管理机构责任制 (1) 按各级技术人员的职责范围&#xff0c;分工负责&#xff0c;做好经常性的技术业务工作。 (2) 组织贯彻执行国家有关技术政策和上级办法的技术标准、规定、规程、和各项技术管理制。 (3) 负责收集和提供技术情报、技术资料、技术建议和技术措施等。 …

什么是状态机?

什么是状态机&#xff1f; 定义 我们先来给出状态机的基本定义。一句话&#xff1a; 状态机是有限状态自动机的简称&#xff0c;是现实事物运行规则抽象而成的一个数学模型。 先来解释什么是“状态”&#xff08; State &#xff09;。现实事物是有不同状态的&#xff0c;例…

Spark3.0中的AOE、DPP和Hint增强

1 Spark3.0 AQE Spark 在 3.0 版本推出了 AQE&#xff08;Adaptive Query Execution&#xff09;&#xff0c;即自适应查询执行。AQE 是 Spark SQL 的一种动态优化机制&#xff0c;在运行时&#xff0c;每当 Shuffle Map 阶段执行完毕&#xff0c;AQE 都会结合这个阶段的统计信…

[工业自动化-19]:西门子S7-15xxx编程 - 软件编程 - PLC程序块、组织块OB与PLC多线程原理、OB、FC、FB、DB

目录 一、PLC的块的种类 1.1 什么是块 1.2 块的种类 1.3 不同块之间的相互调用关系 1.4 OB、FC、FB和DB 二、PLC程序组织块OB 2.1 什么是程序块OB 2.2 为什么需要程序块OB 2.3 PLC有哪些程序块 2.4 如何使用程序块 - OB块的执行顺序和规则 2.5 PLC用户程序主函数&am…

2023.11.12使用flask对图片进行黑白处理(base64编码方式传输)

2023.11.12使用flask对图片进行黑白处理&#xff08;base64编码方式传输&#xff09; 由前端输入图片并预览&#xff0c;在后端处理图片后返回前端显示&#xff0c;可以作为图片处理的模板。 关键点在于对图片进行base64编码的转化。 使用Base64编码可以更方便地将图片数据嵌入…

sass 生成辅助色

背景 一个按钮往往有 4 个状态。 默认状态hover鼠标按下禁用状态 为了表示这 4 个状态&#xff0c;需要设置 4 个颜色来提示用户。 按钮类型一般有 5 个&#xff1a; 以 primary 类型按钮为例&#xff0c;设置它不同状态下的颜色&#xff1a; <button class"btn…

【Android】配置Gradle打包apk的环境

目录 生成jks签名文件 配置build.gradle&#xff08;app&#xff09; 打包 生成jks签名文件 Java 密钥库&#xff08;.jks 或 .keystore&#xff09;是用作证书和私钥存储库的二进制文件。用于为用户设备上安装的 APK 签名的密钥。 详细解释请看官方文档&#xff1a; 为应用…

OpenCV踩坑笔记使用笔记入门笔记整合SpringBoot笔记大全

springboot开启摄像头抓拍照片并上传实现&问题记录 NotAllowedErrot: 请求的媒体源不能使用&#xff0c;以下情况会返回该错误: 当前页面内容不安全&#xff0c;没有使用HTTPS没有通过用户授权NotFoundError: 没有找到指定的媒体通道NoReadableError: 访问硬件设备出错Ov…

Linux线程创建,退出,等待

目录​​​​​​​ 一 为什么使用线程 1.1概念 1.2使用线程的理由 二 线程的创建&#xff0c;退出&#xff0c;等待 2.1 线程创建 2.2 线程退出 2.3.线程等待 2.4. 线程ID获取及比较 一 为什么使用线程 1.1概念 概念&#xff1a;"进程——资源分配的最小单位&…

【算法训练-链表 零】链表高频算法题看这一篇就够了

一轮的算法训练完成后&#xff0c;对相关的题目有了一个初步理解了&#xff0c;接下来进行专题训练&#xff0c;以下这些题目就是汇总的高频题目 题目题干直接给出对应博客链接&#xff0c;这里只给出简单思路、代码实现、复杂度分析 反转链表 依据难度等级分别为反转链表、…

2023数字科技生态展,移远通信解锁新成就

11月10日&#xff0c;以“数字科技&#xff0c;焕新启航”为主题的中国电信2023数字科技生态大会暨2023数字科技生态展在广州盛大启幕。作为物联网行业的龙头标杆&#xff0c;同时更与中国电信连续多年维持稳定友好的合作关系&#xff0c;移远通信受邀参加本次展会。 在本次展会…