【C++】运算符重载(日期类的实现)

news2024/11/25 9:31:11

【C++】运算符重载(日期类的实现)

  • 前言
  • 运算符重载
    • operator
      • 全局和类中
  • 日期类的实现
    • 成员变量的确定
    • 构造函数
      • 拷贝构造
    • 运算符重载部分
      • +=的重载
        • 思路
        • 实现
          • GETmonthday
          • operator+=
      • +的重载
        • 思路
        • 实现
      • -=的与-的重载
        • 实现
      • 各个比较运算符的重载
        • 实现
      • 前置++与后置++
        • 实现
      • (=)赋值运算符的重载
        • 实现
  • 后话

前言

博主在快写完类和对象(中)时,突然发现需要用到运算符重载的知识
但是运算符有很多运算符要进行重载,会导致文章的篇幅略长
所以就单独写了一个,另外,日期类是学习运算符重载比较好的一个例子,所以这里就用了日期类来讲解运算符重载

运算符重载

众所周知,对于内置类型我们可以直接使用运算符进行计算
int y=1, int x=1 x+y
这显然是可以的

那自定义类型可不可以直接用运算符来算呢?
比如
stack s1; stack s2; s1+s2

当然是不可以的

这不是显而易见吗
现在编译器也不至于聪明到能直接分辨出用户的自定义类型的运算。
要是编译器真的高级到这个程度,那就是更高级的语言了

但是类如果要进行加减乘除的时候又要进行函数的调用

如果能直接用加减乘除等操作符的话,那不是能大大增强函数的可读性吗?

所以就有了关键字

operator

运算符重载函数的形式为:
void(返回类型)operator<(需要重载的运算符)(参数)
例:

Date& operator+=(int x);

这里在使用operator进行重载时
有几个点需要注意:

1.不可以用operator重载一个原本没有意义的符号。

如:operator@

2.运算符重载必须有一个自定义类型的对象

因为本来运算符重置就是为了自定义类而创建
要是没有自定义类的对象参数
那还有啥意义。

在这里插入图片描述

这五个运算符不能进行重载。

4.讲了很多次
自定义类型深究到最后,也就只是内置类型

所以当我们编写运算符重载函数的时候:
内置类型的运算符含义不能进行改变

你想改也改不了。

全局和类中

这里来给大家提个问题,全局和类,有啥区别

1. 全局中的函数无法随意调用类中的私人成员

在这里插入图片描述
就会出现这样尴尬的场面(这里有解决方法,但是还是留在之后讲)

但是如果将运算符重载函数写在类中,就可以随意调用了。

2. 类中有this指针这个存在

这意味着运算符重载在全局和类中有不同的定义方式

上面我们看到了:

全局对象需要完全地写出参数

但是在类中的函数因为有this指针的隐形传参,所以不需要将全部参数写出来

class Date
{
	Date& operator+=(int day) 
	{

	}
}

这里this指针将对象的地址传了过去

所以不需要像全局变量一样传一个类的对象形参

所以为了方便,还是尽量讲运算符重载函数写在类中。

日期类的实现

日期算是我们生活中常见的了。

成员变量的确定


年-月-日组成

所以我们这里针对成员变量的设计也是十分容易

class Date
{
private:
	int _year;
	int _month;
	int _day;
};

构造函数

还记得我们这里要创建构造函数的原因吗?

因为date类中全是内置类型
编译器自动生成的默认构造函数不会对内置类型变量进行设定。
所以遇到内置类型就要自己写构造函数了

这里就写个缺省的和无参的

//缺省
 Date(int year,int month,int day)
 {
    _year = year;
	_month = month;
	_day = day;
 }
 //无参
Date()
{
	_year = 2023;
	_month = 5;
	_day = 24;
}

拷贝构造

因为拷贝构造属于构造函数里的,这里就直接把它放在这里了

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

运算符重载部分

这个部分算是正式进入我们的正题了

这里的函数我都默认放在类中进行实现的。

+=的重载

这里要实现的功能是:

日期+=天数 =>(返回)新日期

注意这个运算符号是+=,原本就是要对前一个值进行改变的。
所以我们这里就是对 原对象进行+=,然后返回原对象就好

这里理解起来很容易,但是要实现起来的话还是有点小繁琐的。

思路

想要得到日期+天数转化为日期。

1.首先要得到知道每个月的天数

2.将现在日期的天数和传参的天数相加

3.判断,现在天数是否大于这个月应有的天数

若大于:
扣除每个月对应的天数,然后月份+1

若小于:
则日期已经++完成,直接返回就好

实现

我们要知道所在的这个月的天数,所以我们需要设定一个函数专门用来返回当月的对应的天数

GETmonthday
int GETmonthday(int year, int month)
{
	static int arr[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;
	}
	return arr[month];
}

这里通过静态数组将每个月对应的天数存储下来
(因为这个函数在后续需要多次调用,所以使用静态数组,防止多次开辟空间影响效率)

通过传来的年月,来进行 闰年/平年 对应月 的判断
得到对应的天数后返回。

operator+=
//这里返回值用了引用,因为+=返回的就是原对象,函数结束后没有随着函数被销毁。
Date& operator+=(int day) 
{
	_day += day;
	//将天数和天数相加
	while (_day > this->GETmonthday(_year,_month))
	//当现有天数大于本月总天数,程序继续
	{
		_day -= GETmonthday(_year, _month);
		_month++;
		//天数减去这个月对应天数,当前月份++
		if (_month > 12)
		//当月份大于12时,年++,当前月份变为1
		{
			_month = 1;
			_year++;
		}
	}
	return *this;
	//返回改变后的天数
}

+的重载

思路

+和+=有啥区别?

+=对原对象进行了改变,并且返回的也是原对象

而+ 就不一样了:
不会对原对象进行赋值改变
并且返回的是:原对象与天数相加后的新对象

实现

这里搞懂了区别后就可以进行实现了

这里就会发现:
其实+=和+中间的对象与天数相加的思路一样
就是需要
1.返回新对象
2.原对象不能进行改变。

Date operator+(int day)
{
//用原对象进行了拷贝构造出了tmp
	Date tmp(*this);
	
//将tmp进行+=(用的是之前实现的+=重载)
	tmp += day;
//返回tmp
	return tmp;
}

-=的与-的重载

这里其实思路和+=与+的重载相似,所以这里就不多讲了
因为这篇博客主要是想讲清不同运算符重载的实现

实现

//-=的实现
Date& operator-=(int day)
{
	_day -= day;
	while (_day <= 0)
	{
		_day += GETmonthday(_year, _month);
		_month--;
		if (_month < 1)
		{
			_month = 12;
			_year--;
		}
	}
	return *this;
}
}
//-的实现
Date operator-(int day)
{
	Date tmp(*this);
	tmp -= day;
	return tmp;
}

各个比较运算符的重载

这里比较运算符算是最简单的运算符重载了

传入一个Date类对象
和this指针的本对象进行年月日比较
最后返回布尔值即可

实现

//<的实现
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;
	return false;
}

//大于的实现
bool operator>(const Date& d)
{
	return ((!(*this < d)) && (!(*this == d )));
}

//等于的实现
bool operator==(const Date& d)
{
	return _day == d._day && _year == d._year && _month == d._month;
}
//大于等于的实现
bool operator>=(const Date& d)
{
	return !(*this < d);
}
//小于等于的实现
bool operator<=(const Date& d)
{
	return !(*this > d);
}
//不等于的是实现
bool operator!=(const Date& d)
{
	return !(*this == d);

}

前置++与后置++

这里还是和之前一样,来搞清楚这两个的区别

前置++

直接对象+1,然后返回对象值即可

后置++
先返回对象的原值,然后对象值++
这里的实现思路肯定不是这样,return值了以后函数就结束,怎么还能++

所以这里就要用原对象创建新对象
对原对象++,返回创建的新对象

搞清楚思路了以后,接下来还有个问题

区分两者

还记的运算符重载的命名方式吗

void(返回类型)operator<(需要重载的运算符)(参数)

但是前置++和后置++,都是++运算符

所以定义时都是operator++

这就不好区分了

但是祖师爷在定义的时候就发现

自增运算符都是对单个对象的运算

所以传参时不需要传任何参数
(原对象this指针会进行传递)

那让编译器给前置运算符或者前置运算符
随便传一个参数不就可以识别了吗

所以选择了后置++

Date& Date::operator++()
//前置++
Date Date::operator++(int)
//后置++
//用户自己声明时,要在后置运算符加上int参数

实现

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

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

所以从上面的实现中可以看出

当我们用自定义类型的++时,最好使用前置++

因为后置++需要创建一个新对象,会影响效率。

同样对于内置类型前置++和后置++

虽然也大致是这样的原理
前置++比后置++更加有效率
但是因为内置类型拷贝成本比较低
所以没有特意去区分前置++和后置++的使用

(=)赋值运算符的重载

我们知道,赋值运算符也是默认成员函数之一
就是用户不写时,编译器会自己生成。

目的:将一个对象的值,赋值给另一个对象

实现

在这里插入图片描述

这个赋值构造函数可能看着没啥问题

但是别忘了,对于内置类型来说,赋值能这样做

x=y=c=a
能做到这样的连续赋值。

但是我们的这个实现方法不能进行连续赋值

原因就在于:
返回类型为空

所以这里需要把赋值后的原对象进行返回

Date& operator=(Date& d1)
{
	_year = d1._year;
	_day = d1._day;
	_month = d1._month;
	return (*this);
}

后话

这里其实还有很多内容没有去实现。

比如说
对象与对象的运算符
后置–与前置–
但是这里主要是想带大家学习一下重载运算符而已,
所以这个日期类就点到为止。

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

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

相关文章

接口自动化【六】——接口关联之jsonpath提取+设置全局变量+通用封装

文章目录 前言一、jsonpath提取二、jsonpath与excel当中提取表达式结合三、类的动态属性设置四、设置全局变量&#xff08;这个模块就作为一个讲解&#xff09;五、new_handle_global_data.py六、new_handle_extract.py七、test_new_upload_image.py 文件中上传图片的代码更改八…

mybatis 在当前项目中的实际应用及自定义分页的实现

mybatis 在当前项目中的实际应用及自定义分页的实现 项目中分页代码的解耦 实现目标 实现目标&#xff0c;使用spring 提供的分页相关的类&#xff0c;避免代码中直接使用PageHelper 具体实现 创建自定义PageHelper&#xff0c;并使用spring-data-common提供的具体实现类操…

OTA升级技术概览

随着物联网技术的不断发展&#xff0c;越来越多的设备和系统需要进行远程升级以保持其安全性和功能性。OTA&#xff08;Over-the-Air&#xff09;升级技术是一种通过无线网络远程升级固件或软件的方法&#xff0c;已经成为现代工业、智能家居、汽车等领域中广泛应用的技术。本文…

欧拉公式——最令人着迷的公式之一

欧拉公式是数学里最令人着迷的公式之一&#xff0c;它将数学里最重要的几个常数联系到了一起&#xff1a;两个超越数&#xff1a;自然对数的底e&#xff0c;圆周率π&#xff1b;两个单位&#xff1a;虚数单位i和自然数的单位1&#xff0c;以及数学里常见的0。 ​而且它对数学领…

generate 和 summary 配合——解析 bingchat 逻辑

generate 和 summary 配合——解析 bingchat 逻辑 new bing 微软作为 openai 公司背后的大股东&#xff0c;多年投入一朝开花结果&#xff0c;当然要把 ChatGPT 技术融入到自己的核心产品中&#xff0c;提升整体生产力。微软的第一个措施&#xff0c;就是在必应搜索引擎 bing…

chatgpt赋能Python-python_span_抓取

介绍 随着互联网的不断发展&#xff0c;SEO&#xff08;搜索引擎优化&#xff09;已成为所有网站主人必须面对的问题。在SEO中&#xff0c;抓取是一个非常重要的环节&#xff0c;也是一个关键性的步骤&#xff0c;它直接影响到网站的排名。 在Python编程中&#xff0c;有很多…

Mac上安装多个版本的MySQL

文章目录 准备工作先确定自己机器是多少位的下载包 具体步骤1. 先安装低版本的MySQL2. 清理完后&#xff0c;再安装高版本的MySQL3. 将低版本的文件夹移回 /usr/local4. 切换版本5. 验证 扩展清理命令其他信息 准备工作 先确定自己机器是多少位的 uname -a输出X86_64&#xf…

【Error】Python3.7 No module named ‘_sqlite3‘ 解决方案

场景&#xff1a;docker容器运行keybert时出现错误 No module named ‘_sqlite3‘&#xff0c;是容器环境没有sqlite的库&#xff0c;如下图所示&#xff1a; 本机是能够正常导入sqlite3的&#xff0c;虚拟环境conda下也有该库。 python3.8版本的不可用于python3.7中&#xff0…

【LeetCode】169. 多数元素

169. 多数元素&#xff08;简单&#xff09; 方法一&#xff1a;sort排序&#xff0c;时间复杂度为O(nlogn) 思路 我自己的写法用了最简单的方法&#xff0c;首先使用 sort() 对数组元素按照从小到大进行排序&#xff0c;然后依次遍历每个元素&#xff0c;如果该元素的出现次…

CompletableFuture 异步编排如何使用?

概述&#xff1a; 在做提交订单功能时&#xff0c;我们需要处理的事务很多&#xff0c;如&#xff1a;修改库存、计算优惠促销信息、会员积分加减、线上支付、金额计算、生成产品订单、生成财务信息、删除购物车等等。如果这些功能全部串行化处理&#xff0c;会发费很长的时间…

【盘点】界面控件DevExpress WPF的几大应用程序主题

DevExpress WPF控件包含了50个应用程序主题和40个调色板&#xff0c;用户可以在发布应用程序是指定主题&#xff0c;或允许最终用户动态修改WPF应用程序的外观和样式&#xff0c;其中主题带有调色板&#xff0c;可以进一步个性化您的UI&#xff01; PS&#xff1a;DevExpress …

oracle WAITED TOO LONG FOR A ROW CACHE ENQUEUE LOCK问题分析

服务概述 财务系统出现业务卡顿&#xff0c;数据库实例2遇到>>> WAITED TOO LONG FOR A ROW CACHE ENQUEUE LOCK! <<<错误导致业务HANG住。 对此HANG的原因进行分析&#xff1a; 故障发生时&#xff0c;未有主机监控数据&#xff0c;从可以获取的数据库A…

c++ 11标准模板(STL) std::map(三)

定义于头文件<map> template< class Key, class T, class Compare std::less<Key>, class Allocator std::allocator<std::pair<const Key, T> > > class map;(1)namespace pmr { template <class Key, class T, clas…

使用Linkage Mapper工进行物种分布建模的步骤详解(含实际案例分析)

✅创作者:陈书予 🎉个人主页:陈书予的个人主页 🍁陈书予的个人社区,欢迎你的加入: 陈书予的社区 🌟专栏地址: Linkage Mapper解密数字世界链接 文章目录 引言:一、介绍二、数据准备2.1 物种分布数据获取2.2 环境变量数据获取2.3 数据预处理

【拼多多API 开发系列】百亿补贴商品详情接口,代码封装

为了进行电商平台 PDD 的API开发&#xff0c;首先我们需要做下面几件事情。 1&#xff09;开发者注册一个账号 2&#xff09;然后为每个 PDD 应用注册一个应用程序键&#xff08;App Key) 。 3&#xff09;下载 PDD API的SDK并掌握基本的API基础知识和调用 4&#xff09;利用SD…

CSS布局:浮动与绝对定位的异同点

CSS布局&#xff1a;浮动与绝对定位的异同点_cherry_vincent的博客-CSDN博客 浮动 ( float ) 和绝对定位 ( position:absolute ) 相同点&#xff1a; &#xff08;1&#xff09;都是漂起来( 离开原来的位置 ) &#xff08;2&#xff09;并且都不占着原来的位置 &#xff08;3…

Flutter 笔记 | Flutter 布局组件

布局类组件都会包含一个或多个子组件&#xff0c;布局类组件都是直接或间接继承SingleChildRenderObjectWidget 和MultiChildRenderObjectWidget的Widget&#xff0c;它们一般都会有一个child或children属性用于接收子 Widget。 不同的布局类组件对子组件排列&#xff08;layo…

企业级WordPress开发 – 创建企业级网站的优秀提示

目录 “企业级”是什么意思&#xff1f; 使用WordPress创建企业级网站有什么好处&#xff1f; 使用 WordPress 进行企业开发的主要好处 WordPress 可扩展、灵活且价格合理 WordPress 提供响应式 Web 开发 WordPress 提供了巨大的可扩展性 不断更新使 WordPress 万无一…

JAVA-创建PDF文档

目录 一、前期准备 1、中文字体文件 2、maven依赖 二、创建PDF文档方法 三、通过可填充PDF模板将业务参数进行填充 1、 设置可填充的PDF表单 2、代码开干&#xff0c;代码填充可编辑PDF并另存文件 一、前期准备 1、中文字体文件 本演示使用的是iText 7版本&#xff0c…

Jeddak-DPSQL 首次开源!基于差分隐私的 SQL 代理保护能力

动手点关注 干货不迷路 ‍ ‍1. 背景 火山引擎对于用户敏感数据尤为重视&#xff0c;在火山引擎提供的数据分析产品中&#xff0c;广泛采用差分隐私技术对用户敏感信息进行保护。此类数据产品通常构建于 ClickHouse 等数据引擎之上&#xff0c;以 SQL 查询方式来执行计算逻辑&a…