C++运算符重载详解(日期类实操)

前言:为什么要实现运算符重载?

在C语言中,对于内置类型,我们可以根据符号>、<、==等去直接比较大小,但是对于自定义来说,肯定不能直接比较大小,例如下面的日期类,想要比较两个两个日期的大小,或者相差多少天,直接写运算符肯定是不行的。

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

这时候就需要运算符重载出马了!

运算符重载的概念:

运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类

型与参数列表与普通的函数类似。

函数名字为:

关键字operator后面接需要重载的运算符符号。

函数原型:

返回值类型 operator操作符(参数列表)。

注意:

  • 不能通过连接其他符号来创建新的操作符:比如operator@ 
  • 重载操作符必须有一个类类型参数
  • 用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义
  • 作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this
  • .* :: sizeof ?: . 注意以上5个运算符不能重载。这个经常在笔试选择题中出现。

日期类运算符实操

头文件(函数的声明)

class Date
{
public:
	// 获取某年某月的天数
	int GetMonthDay(int year, int month);
	//打印日期
	void Print();

	// 全缺省的构造函数
	Date(int year = 1900, int month = 1, int day = 1);
	// 拷贝构造函数
  // d2(d1)
	Date(const Date& d);
	// 赋值运算符重载
  // d2 = d3 -> d2.operator=(&d2, d3)
	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;
};

重载实现(函数的定义):

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

void Date::Print()
{
	cout << _year << '-' << _month << '-' << _day;
}


//构造函数的参数声明给,定义不给
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;
}

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

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

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

bool Date::operator>=(const Date& d)
{
	return operator==(d) || operator>(d);
}

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

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


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

//前置++和后置++的区别就是构成重载,参数加上int
//在调用的时候,有参数会调用前置++,没有参数会调用后置++
Date& Date::operator++()
{
	(*this) += 1;
	return *this;
}

 // 注意:后置++是先使用后+1,因此需要返回+1之前的旧值,故需在实现时需要先将this保存一份,然后给this+1
 //       而temp是临时对象,因此只能以值的方式返回,不能返回引用


Date Date::operator++(int)
{
	//调用拷贝构造函数
	Date tmp(*this);
	(*this) += 1;
	return tmp;
}

Date& Date::operator-=(int day)
{
	_day -= day;
	while (_day <= 0)
	{
		_month--;
		if (_month == 0)
		{
			_year--;
			_month = 12;
		}
		_day += GetMonthDay(_year, _month);
	}
	return *this;
}


int Date::operator-(const Date& d)
{
	int cnt = 0;
	while (*this != d)
	{
		cnt++;
		(*this)--;
	}
	return cnt;
}

Date& Date::operator--()
{
	(*this) -= 1;
	return *this;
}

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

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++;
		}
	}
	return tmp;
}

重载过程中的注意点:

  • 构造函数如果声明和定义分离,声明需要将参数赋缺省值,定义不需要写,以防混淆。
  • 传参数时尽量都要用引用传参,可以提高传参的效率
  • 传返回值时,如果返回值在调用完这个函数没有被销毁,需要引用返回,如果销毁了,直接返回。
  • 复用已经实现的函数

赋值运算符重载:

1. 赋值运算符重载格式

  • 参数类型:const T&,传递引用可以提高传参效率
  • 返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值检测是否自己给自己赋值
  • 返回*this :要复合连续赋值的含义

2、前置++后置++的重载区别


 // 前置++和后置++都是一元运算符,为了让前置++与后置++形成能正确重载
 // C++规定:后置++重载时多增加一个int类型的参数,但调用函数时该参数不用传递,编译器自动传递
 // 注意:后置++是先使用后+1,因此需要返回+1之前的旧值,故需在实现时需要先将this保存一份,然后给this+1(调用拷贝构造函数
 //       而temp是临时对象,因此只能以值的方式返回,不能返回引用

this指向的对象函数结束后不会销毁,故以引用方式返回提高效率

输入流输出流操作符重载

为什么cin cout能够自动识别任意类型的数据呢?

本质上就是函数的重载

原码:

ostream& operator<<(ostream& out, const Date& d)
{
	out << d._year << "年" << d._month << "月" << d._day << "日" << endl;

	return out;
}

istream& operator>>(istream& in, const Date& d)
{
	in >> d._year >> d._month >> d._day;

	return in;
}

总结:

其他的运算符一般是实现成成员函数,<< >>运算符必须是实现到全局,这样才能让流对象做第一个参数,才符合可读性。不然可读性很差,像下面定义在类的内部:

重载流操作符为什么必须用引用?

如果我们使用值传送来传递一个流给函数,那么在函数里要生成一个该流的临时变量,生成临时变量的时候,就要调用对应的拷贝构造函数,并且这个拷贝构造函数必须是以一个值传送的流作为参数的——但是流就是没有这样的拷贝构造函数

分析:

流本质是为了解决,自定义类型的输入和输出问题,printf scanf 无法解决自定义类型的输入输出问题

面向对象 + 运算符重载解决!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

移动机器人路径规划(三)--- 基于采样的路径规划Sample-basedpath finding

目录 1 基于采样的路径规划的优点和一些重要概念 2 概率路图 Probabilistic Road Map 3 快速搜索随机树Rapidly-exploring Random Tree 3.1 RRT 3.2 RRT Connect 4 RRT算法的优化 4.1 RRT* 4.2 Kinodynamic-RRT* 4.3 Anytime-RRT* 5 Advanced Sampling-based Methods…

PCL 点云超体素分割-SupervoxelClustering

一、概述 原始文档与点云 Clustering of Pointclouds into Supervoxels - Theoretical primer — Point Cloud Library 0.0 documentation 超像素 Superpixels Segmentation algorithms aim to group pixels in images into perceptually meaningful regions which conform…

【Linux】文件系统中inode与软硬链接以及读写权限问题

文章目录 前言一、 简单理解文件系统二、文件操作具体步骤1.新建文件2.删除文件3.查找文件 三、目录的重新理解1.目录下没有w权限&#xff0c;无法对其下的文件进行创建与删除2.目录下没有r权限&#xff0c;无法对其下的文件进行查看3.目录下没有x权限&#xff0c;无法进入这个…

空调能量表

数字化应用场景&#xff1a;空调能量监测 定义 空调能量表产品又被称为冷量积算仪、冷量积分仪、能量积分仪、能量积算仪、空调冷热量表、冷量表、能量表等&#xff0c;现阶段行业内没有统一的名称。 作用 用于计量中央空调能耗的仪表&#xff0c;它通过和空调管道流量计和温…

numpy数据库

numpy中的数组 0、导包 import numpy as np 1、创建数组 >>> # 创建数组&#xff0c;得到darray类型 >>> t1 np.array([1, 2, 3]) >>> t2 np.array(range(8)) >>> t3 np.arange(1, 9, 2) 2、数组为 numpy.ndarray 类型 >>…

基于单片机C51全自动洗衣机仿真设计

**单片机设计介绍&#xff0c; 基于单片机C51全自动洗衣机仿真设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机C51的全自动洗衣机仿真设计是一个复杂的项目&#xff0c;它涉及到硬件和软件的设计和实现。以下是对这…

redis常见问题及解决方案

缓存预热 定义 缓存预热是一种优化方案&#xff0c;它可以提高用户的使用体验。 缓存预热是指在系统启动的时候&#xff0c;先把查询结果预存到缓存中&#xff0c;以便用户后面查询时可以直接从缓存中读取&#xff0c;节省用户等待时间 实现思路 把需要缓存的方法写在初始化方…

Linux三剑客:grep的基本使用

目录 grep介绍 什么是grep和egrep 使用grep 命令格式 命令功能 命令参数 grep配合正则表达式使用 认识正则 基本正则表达式 匹配字符 配置次数 位置锚定&#xff1a;定位出现的位置 分组和后向引用 作为学习一名计算机专业的学生&#xff0c;我想Linux应该需要了解…

HTML5学习系列之实用性标记

HTML5学习系列之实用性标记 前言实用性标记高亮显示进度刻度时间联系信息显示方向换行断点标注 总结 前言 学习记录 实用性标记 高亮显示 mark元素可以进行高亮显示。 <p><mark>我感冒了</mark></p>进度 progress指示某项任务的完成进度。 <p…

Python基础教程之模块介绍及用法,适合新手小白的入门教程~

文章目录 什么是模块&#xff1f;创建模块使用模块模块中的变量为模块命名重命名模块内建模块使用 dir() 函数从模块导入 什么是模块&#xff1f; 请思考与代码库类似的模块。 模块是包含一组函数的文件&#xff0c;希望在应用程序中引用。 创建模块 如需创建模块&#xff…

(C++类的初始化和清理)构造函数与析构函数

目录 1. 类的六个默认成员函数2. 构造函数&#xff08;Constructor&#xff09;2.1 概念2.2 特性 3. 析构函数&#xff08;Destructor&#xff09;3.1 概念3.2 特性 1. 类的六个默认成员函数 一个类中如果什么成员都没有&#xff0c;称为空类 class Date {};但是这并不代表空…

Windows 系统彻底卸载 SQL Server 通用方法

Windows 系统彻底卸载 SQL Server 通用方法 无论什么时候&#xff0c;SQL Server 的安装和卸载都是一件让我们头疼的事情。因为不管是 SQL Server 还是 MySQL 的数据库&#xff0c;当我们在使用数据库时因为未知原因出现问题&#xff0c;想要卸载重装时&#xff0c;如果数据库…

如何分析伦敦金的价格走势预测?

伦敦金作为国际黄金市场的重要指标&#xff0c;其价格走势一直备受投资者关注。但是&#xff0c;黄金市场的价格变化受到多种因素的影响&#xff0c;因此要准确预测伦敦金的价格走势并非易事。在本文中&#xff0c;将介绍一些常用的方法和工具&#xff0c;帮助您分析伦敦金的价…

Docker-compose 下载安装测试完成

源文件-http://t.csdnimg.cn/7NxHchttp://t.csdnimg.cn/7NxHc 1 docker-compose说明 Docker Compose 是Docker的组装工具&#xff0c;用于创建和调试多个Docker容器&#xff0c;并在同一个Docker主机上运行它们。Docker Compose基于YAML文件&#xff0c;描述多个容器之间的相…

在Spring Boot中使用Redis的发布订阅功能

Redis的发布订阅模式是一种消息传递模式&#xff0c;它允许多个订阅者订阅一个或多个频道&#xff0c;同时一个发布者可以将消息发布到指定的频道。这种模式在分布式系统中非常有用&#xff0c;可以解决以下问题&#xff1a; 实时消息传递&#xff1a;发布订阅模式可以用于实时…

django——公众号服务开发

开发过程 项目背景&#xff1a;功能描述&#xff1a;参考文档以及调试链接&#xff1a;技术架构&#xff1a;准备工作公众号的注册以及设置域名的准备服务器的租赁内网穿透微信支付的注册 功能开发细节微信公众号自定义菜单获取access_token创建菜单查询菜单删除菜单 个性化菜单…

Nginx反向代理与负载均衡与504错误

Nginx反向代理与负载均衡概念简介 关于代理 什么是代理 类似中介 在没有代理模式的情况下&#xff0c;客户端和Nginx服务端&#xff0c;都是客户端直接请求服务端&#xff0c;服务端直接响应客户端。 那么在互联网请求里面&#xff0c;客户端往往无法直接向服务端发起请求…

【LeetCode刷题-滑动窗口】--76.最小覆盖子串

76.最小覆盖子串 class Solution {//建立两个hashMap&#xff0c;ori用于存储目标字符串t中每个字符的出现次数//cnt用于存储当前窗口中每个字符的出现次数Map<Character,Integer> ori new HashMap<Character,Integer>();Map<Character,Integer> cnt new H…

PyTorch:计算图

在深度学习和神经网络领域&#xff0c;计算图是一种重要的概念&#xff0c;它在理解和实现神经网络模型的训练过程中起着至关重要的作用。PyTorch作为一款优秀的深度学习框架&#xff0c;自然也包含了计算图的概念和实现。本文将深入探讨PyTorch中计算图的原理、应用以及对深度…

mp4封装格式各box类型讲解及IBP帧计算

作者 —— 靑い空゛ 出处&#xff1a;http://www.cnblogs.com/ailumiyana/ 音视频流媒体高级开发教程 MP4文件封装格式&#xff0c;对应的标准为ISO/IEC 14496-12&#xff0c;即信息技术 视听对象编码的第12部分 ISO 基本媒体文件格式&#xff08;Information technology Codi…