【C++ | 重载运算符】一文弄懂C++运算符重载,怎样声明、定义运算符,重载为友元函数

news2024/11/29 0:42:30

😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀
🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C++、数据结构、音视频🍭
⏰发布时间⏰:2024-06-21 22:14:42

本文未经允许,不得转发!!!

目录

  • 🎄一、为什么需要重载运算符
  • 🎄二、怎样声明、定义重载运算符函数
    • ✨2.1 重载运算符的限制
    • ✨2.2 重载运算符定义
    • ✨2.3 例子-重载赋值运算符
  • 🎄三、重载为友元函数
  • 🎄四、总结



在这里插入图片描述

🎄一、为什么需要重载运算符

正常情况下,C++ 的运算符( +、-、*、/ 等)只能用于对基本类型的常量或变量进行运算,而不能用于类对象之间的运算。

类的对象直接的运算虽然可以通过成员函数或全局函数去实现,如date.Add(1),但这样的写法有时不易理解。

C++的提供了 重载运算符 的特性,允许将一些常见的运算符根据自定义类型的需要去重新定义,尽量让对象之间的运算可以像基本类型的运算一样简单、好理解。

运算符重载的目的是使得 C++ 中的运算符也能够用来操作对象。

下面例子演示了CDate类对象 使用 Add函数与 重载运算符operator+ 的运算结果:

// g++ 19_operatorAdd.cpp 
#include <iostream>
using namespace std;

class CDate
{
public:
	CDate(int year, int mon, int day);	// 构造函数声明
	CDate(const CDate& date);			// 拷贝构造函数声明
	CDate Add(int day);					// Add成员函数声明
	CDate operator+(int day);			// 加号运算符声明
	void show()
	{
		cout << "Date: " << m_year << "." << m_mon << "." << m_day << endl;
	}

private:
	int m_year;
	int m_mon;
	int m_day;
};

// 构造函数定义
CDate::CDate(int year, int mon, int day)
{
	m_year = year;
	m_mon = mon;
	m_day = day;
	//cout << "Calling Constructor" << ", this=" << this <<endl;
}

// 拷贝构造函数定义
CDate::CDate(const CDate& date)
{
	m_year = date.m_year;
	m_mon = date.m_mon;
	m_day = date.m_day;
	//cout << "Calling Copy Constructor" << ", this=" << this << ", Copy Data" <<endl;
}

// Add成员函数定义
CDate CDate::Add(int day)
{
	CDate temp = *this;
	temp.m_day += day;
	cout << "Calling CDate::Add" << ", this=" << &temp << endl;
	return temp;
}

// 加号运算符定义
CDate CDate::operator+(int day)
{
	CDate temp = *this;
	temp.m_day += day;
	cout << "Calling operator+(int)" << ", this=" << &temp << endl;
	return temp;
}

int main()
{
	CDate date(2024,6,20);
	date.show();
	cout << endl;
	
	CDate date1 = date.Add(1);		// 成员函数
	date1.show();
	cout << endl;
	
	CDate date2 = date.operator+(1);// 直接调用运算符函数
	date2.show();
	cout << endl;
	
	CDate date3 = date + 1;			// 好理解
	date3.show();
	cout << endl;
	
	return 0;
}

运行结果如下:
在这里插入图片描述
例子中两个成员函数Addoperator+的实现是一样的,但三种调用方式中,date + 1会比较好理解。

例子中的date.operator+(1);等价于date + 1;在运算符表示法中,运算符左侧的对象(这里为 date)是调用对象,运算符右边的对象(这里为 1) 是作为参数被传递的对象


在这里插入图片描述

🎄二、怎样声明、定义重载运算符函数

重载运算符,本质上也是函数的一种,使用关键字operator加上运算符作为函数名,运算符函数的格式大致如下:

返回值类型 operator 运算符(形参列表)
{
	...
}

函数名是清楚了,那形参怎样写呢? 返回值类型应该是什么类型?这小节的剩下内容会解决这两个问题。

✨2.1 重载运算符的限制

清楚了重载运算符的大致格式之后,再来了解一下C++对重载运算符的限制:

  • 1、重载后的运算符必须至少有一个操作数是用户定义的类型(类、结构体、枚举),这样可以防止给基本类型重载运算符;
  • 2、使用运算符时不能违反运算符原来的句法规则,例如,不能将加号(+)重载为减法运算。也不能修改运算符优先级;
  • 3、不能创建新运算符,例如,不能定义 operator **( )函数来表示求幂。
  • 4、不能重载下面的运算符:
    • sizeof:sizeof 运算符。
    • .:成员运算符。
    • .*:成员指针运算符。
    • :::作用域解析运算符。
    • ?::条件运算符。
    • typeid:—个 RTTI 运算符。
    • const_cast:强制类型转换运算符。
    • dynamic_cast:强制类型转换运算符。
    • reinterpret_cast:强制类型转换运算符。
    • static_cast:强制类型转换运算符。
  • 5、下面的运算符只能通过成员函数进行重载
    • =: 赋值运算符。
    • ( ):函数调用运算符。
    • [ ]:下标运算符。
    • ->:通过指针访问类成员的运算符。

在这里插入图片描述


✨2.2 重载运算符定义

我们知道重载运算符的函数名是:operator运算符,那么形参个数和类型应该是什么?

重载运算符函数形参

重载运算符函数的参数数量与该运算符作用的运算对象数量一样多。一元运算符有一个参数, 二元运算符有两个。对于二元运算符来说,左侧运算对象传递给第一个参数,而右侧运算对象传递给第二个参数。
如果一个运算符函数是成员函数, 则它的第一个(左侧)运算对象绑定到隐式的 this 指针,因此,成员运算符函数的(显式)参数数量比运算符的运算对象总数少一个。

从上面这段话可以知道,重载运算符函数的形参由运算符的操作数的个数、类型决定的。如果是成员函数,则左边操作对象的类型的形参会省略,以this指针传到运算符函数。以CDate类为例:

CDate date(2024,6,20);
CDate date1(2024,6,20);
CDate date2 = date + date1;	// 形参列表是:(const CDate &date, const CDate &date1)
CDate date2 = date + 1;		// 形参列表是:(const CDate &date, int i)
CDate date2 = date + 1.5;	// 形参列表是:(const CDate &date, double d)

重载运算符函数返回值类型
重载运算符的返回类型通常情况下应该与其内置版本的返回类型兼容:逻辑运算符和关系运算符应该返冋 bool,算术运算符应该返回一个类类型的值,赋值运算符和复合赋值运算符则应该返冋左侧运算对象的一个引用。

下面代码以重载加号运算符为例,这里是让一个CDate对象加上一个整型:

// 加号运算符定义
CDate CDate::operator+(int day)
{
	CDate temp = *this;
	temp.m_day += day;
	cout << "Calling operator+(int)" << ", this=" << &temp << endl;
	return temp;
}

✨2.3 例子-重载赋值运算符

赋值运算符的重载也是C++的一个特别重要的知识点,可以重载为 拷贝赋值运算符 、移动赋值运算符 两种,需要详细了解的可以参考这两篇文章:
【C++ | 拷贝赋值运算符函数】一文了解C++的 拷贝赋值运算符函数
【C++ | 移动赋值运算符】一文了解C++11的 移动赋值运算符

这个小节只介绍怎样声明、定义赋值运算符,拷贝赋值运算符声明、定义如下。赋值运算符只能重载为函数名为operator=,其形参都是一个const CDate &类型的,返回值是该类型的引用(这样支持连续赋值a=b=c):

CDate& operator=(const CDate &date);// 拷贝赋值运算符声明
// 赋值运算符函数定义
CDate& CDate::operator=(const CDate& date)
{
	if(this == &date)	// 赋值给自身
		return *this;
	delete [] str;		// 释放旧的数据
	m_year = date.m_year;
	m_mon = date.m_mon;
	m_day = date.m_day;
	cout << "Calling operator=" << ", this=" << this << ", Copy Data" <<endl;
	return *this;
}

在这里插入图片描述

🎄三、重载为友元函数

如果一个运算符的左操作数是类对象,则这个运算符一般是可以重载为成员函数的,例如上文的date+1。那如果表达式是1+date,则上面重载的加号运算符operator+匹配不到这个表达式。这是我们就需要将重载运算符写到类外部,而类外定义的函数无法访问类的私有成员,所以又需要将这个类外定义的函数声明为类的友元函数。关于C++的友元,有不了解的,可以参考这篇文章:C++ | 友元(friend)】友元函数、友元类、友元成员函数详解及例子代码。

下面看看怎样将加号运算符重载为友元函数:

  • 1、看要实现怎样的操作。这里是要实现1+date
  • 2、确定重载运算符的形参,1+date的第一个操作数是int型,第二个操作数是CDate,所以形参可以是:(int i, const CDate &date)
  • 3、确定返回值类型,这里我们就直接返回 CDate 类,所以函数原型如下;
    CDate operator+(int day, const CDate &date);
    
  • 4、写具体函数实现,我们这里想要的是,让天数加上 i ,完整实现如下:
    // 友元函数定义
    CDate operator+(int day, const CDate &date)
    {
    	CDate temp = date;
    	temp.m_day += day;
    	cout << "Calling operator+(int, CDate)" << ", temp=" << &temp << endl;
    	return temp;
    }
    
  • 5、将函数声明为类的友元函数,在类声明开头使用friend如下声明:
    class CDate
    {
    	friend CDate operator+(int day, const CDate &date);	// 友元函数声明
    	...
    }
    

重载运算符时,优先重载为成员函数,如果不能重载为成员函数,就重载为友元函数。
编译器查找运算符函数的顺序是,先去类中查找匹配的成员函数,如果找不到就到全局函数寻找。


在这里插入图片描述

🎄四、总结

👉本文主要介绍了为什么需要重载运算符,重载运算符有哪些限制,怎样声明、定义运算符函数,怎样将运算符重载为友元函数。

上面例子的完整代码:

// g++ 19_operator.cpp 
#include <iostream>
using namespace std;

class CDate
{
	friend CDate operator+(int day, const CDate &date);	// 友元函数声明
public:
	CDate(int year, int mon, int day);	// 构造函数声明
	CDate(const CDate& date);			// 拷贝构造函数声明
	CDate& operator=(const CDate &date);// 拷贝赋值运算符声明
	CDate operator+(int day);			// 加号运算符声明
	CDate operator+(const CDate& date);	// 加号运算符声明
	
	void show()
	{
		cout << "Date: " << m_year << "." << m_mon << "." << m_day << endl;
	}

private:
	int m_year;
	int m_mon;
	int m_day;
};

// 构造函数定义
CDate::CDate(int year, int mon, int day)
{
	m_year = year;
	m_mon = mon;
	m_day = day;
	//cout << "Calling Constructor" << ", this=" << this <<endl;
}

// 拷贝构造函数定义
CDate::CDate(const CDate& date)
{
	m_year = date.m_year;
	m_mon = date.m_mon;
	m_day = date.m_day;
	//cout << "Calling Copy Constructor" << ", this=" << this << ", Copy Data" <<endl;
}

// 赋值运算符函数定义
CDate& CDate::operator=(const CDate& date)
{
	if(this == &date)	// 赋值给自身
		return *this;
	m_year = date.m_year;
	m_mon = date.m_mon;
	m_day = date.m_day;
	cout << "Calling operator=" << ", this=" << this << ", Copy Data" <<endl;
	return *this;
}

// 加号运算符定义
CDate CDate::operator+(int day)
{
	CDate temp = *this;
	temp.m_day += day;
	cout << "Calling operator+(int)" << ", this=" << &temp << endl;
	return temp;
}

// 加号运算符定义
CDate CDate::operator+(const CDate& date)
{
	CDate temp = *this;
	temp.m_day += date.m_day;
	cout << "Calling operator+(const CDate&)" << ", this=" << &temp << endl;
	return temp;
}

// 友元函数定义
CDate operator+(int day, const CDate &date)
{
	CDate temp = date;
	temp.m_day += day;
	cout << "Calling operator+(int, CDate)" << ", temp=" << &temp << endl;
	return temp;
}

int main()
{
	CDate date(2024,6,20);
	date.show();
	cout << endl;

	CDate date1 = date + 1;
	date1.show();
	cout << endl;
	
	CDate date2 = date + date1;
	date2.show();
	cout << endl;
	
	date2 = 1+date;
	date2.show();
	cout << endl;
	
	return 0;
}

在这里插入图片描述
如果文章有帮助的话,点赞👍、收藏⭐,支持一波,谢谢 😁😁😁

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

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

相关文章

从艳彩山水到艳彩艺术 薛永年:郭泰来艳彩艺术填补了中国美术史的空白

薛永年先生 自6月12日开展以来&#xff0c;郭泰来现代艺术大展杭州如火如荼地进行着&#xff0c;吸引了众多艺术爱好者和专业人士前往。毫不夸张地说&#xff0c;总统和清洁工人都能在他的作品中找到自己心中的那一块共振带并与之产生强烈的共鸣&#xff0c;这便是郭泰来先生的…

如何禁止学生开启windows防火墙

信息课上&#xff0c;学生最喜欢开启windows防火墙来脱离电子教室的控制&#xff0c;如何禁止学生开启Windows防火墙&#xff0c;可以从以下几个方面入手&#xff1a; 一、使用组策略编辑器 打开组策略编辑器&#xff1a;按下WinR键&#xff0c;输入gpedit.msc并回车&#xf…

眼在手上标定结果应用:像素坐标转机械臂世界坐标

像素坐标转世界坐标主要用到如下的公式&#xff1a;其中boar2camera矩阵可由通过拍摄的标定板图片直接求解&#xff0c;为相机内参矩阵 camera_matrix: rows: 3 cols: 3 data: [428.3066849046146, 0, 675.2344606795484, 0, 431.0838735333736, 405.3373367752419, 0, 0, 1] …

cron.timezone

系统 date 数据库 show timezone插件 show cron.timezonealter system set cron.timezonePRC;show cron.timezone

NSSCTF-Web题目13

目录 [SWPUCTF 2022 新生赛]js_sign 1、题目 2、知识点 3、思路 [MoeCTF 2021]Do you know HTTP 1、题目 2、知识点 3、思路 [SWPUCTF 2022 新生赛]js_sign 1、题目 2、知识点 base64编码、敲击码&#xff08;tap code&#xff09; 3、思路 页面没有什么&#xff0c;…

fataadmin导出Exel文件图片太大

// 导出图片过大处理 exportOptions: {ignoreColumn: [0, operate],onBeforeSaveToFile: function (data, fileName, type, charset, encoding, bom) {return $.fn.bootstrapTable.defaults.extend.savestatus;},onCellHtmlHyperlink: function ($cell, rowIndex, colIndex, hr…

vue2 + Lodop 制作可视化设计页面 实现打印设计功能(一)

前言&#xff1a; 此功能的来源来自于当时需要制作一个便于客户操作的打印设计功能&#xff0c;然后就有了这个项目。这个帖子主要是用于分享与谈论&#xff0c;相互学习。 目标&#xff1a; 能在vue页面中拖拽组件支持批量操作拖动通过拖拽组件列表里的组件到page进行添加实…

四,SSM整合-前后端分离(实现分页+前后端校验)

分页与校验 实现功能07-分页显示列表需求分析/图解思路分析代码实现完成测试 实现功能08-带条件查询分页显示列表需求分析/图解思路分析代码实现 实现功能09-添加家居表单前端校验需求分析/图解思路分析代码实现 实现功能10-添加家居表单后端校验需求分析/图解思路分析代码实现…

普通变频器位置闭环控制(S7-1200PLC工艺对象模拟量轴)

1、S7-1200PLC控制V90总线伺服通过工艺对象实现定位控制 S7-1200PLC和V90总线伺服通过工艺对象实现定位控制(标准报文3应用)_1200报文3控制v90-CSDN博客文章浏览阅读182次。V90伺服驱动器调试软件SINAMICS V-ASSISTANT Commissioning tool下载地址如下:西门子官网选型|资料CS…

【Kafka】Kafka Broker工作流程、节点服役与退役、副本、文件存储、高效读写数据-08

【Kafka】Kafka Broker工作流程、节点服役与退役、副本、文件存储、高效读写数据 1. Kafka Broker 工作流程1.1 Zookeeper 存储的 Kafka 信息1.2 Kafka Broker总体工作流程1.2.1 Controller介绍 1.3 Broker 重要参数 2. 节点服役与退役3. Kafka副本 1. Kafka Broker 工作流程 …

Matplotlib绘图9种经典风格,你喜欢哪种?

学过Python的小伙伴都会知道&#xff0c;Matplotlib是Python生态最好用的可视化工具库&#xff0c;吹爆也不为过。&#x1f44d; Matplotlib作为高度定制化的绘图工具&#xff0c;只要你使用Python编程便可完美绘制二维统计图表、三维图表、动态图表、交互图表&#xff0c;甚至…

找不到d3dx9_43.dll无法继续执行代码的几种解决方法

在工作或生活使用电脑都会遇到丢失dll文件应用无法启动的情况&#xff0c;比如你安装完一款你最喜欢的游戏在启动的时候提示系统缺少d3dx9_39.dll、d3dx9_40.dll、d3dx9_41.dll、d3dx9_42.dll、d3dx9_43.dll、xinput1_3.dll 文件而无法正常游戏&#xff0c;或你在工作的时候安装…

vue:vue2与vue3如何全局注册公共组件(包括涉及到的相关方法函数的讲解)

目录 第一章 vue2全局注册公共组件 1.1 方法一&#xff1a;逐个注册 1.2 方法二&#xff1a;批量注册 1.2.1 require.context()方法解释 第二章 vue3全局注册公共组件 1.1 方法一&#xff1a;逐个注册 1.2 方法二&#xff1a;批量注册 第一章 vue2全局注册公共组件 Vue…

Golang并发控制的三种方案

Channel Channel是Go在语言层面提供的一种协程间的通信方式&#xff0c;我们可以通过在协程中向管道写入数据和在待等待的协程中读取对应协程的次数来实现并发控制。 func main() {intChan : make(chan int, 5)waitCount : 5for i : 0; i < waitCount; i {go func() {intC…

【计算机网络体系结构】计算机网络体系结构实验-FTP实验

1. 2. 3. wireshark 第一行&#xff1a;帧Frame 545&#xff1a;要发送的数据块&#xff0c;所抓帧的序号为545&#xff0c;捕获字节数等于传送字节数&#xff1a;451字节第二行&#xff1a;源Mac地址为a4:bb:6d:6e:28:9a&#xff1b;目标Mac地址为24:00:fa:e4:df:d8第三行&…

安卓逆向案例——X酷APP逆向分析

X酷APP逆向分析 这里介绍一下两种不同的挂载证书的方法。 chls.pro/ssl无法在浏览器中下载证书是什么原因解决方法&#xff1a; 法一 1. 挂载系统分区为读写 使用正确的挂载点来挂载系统分区为读写&#xff1a; su mount -o remount,rw /dev/uijISjR/.magisk/block/syste…

Spring Boot连接Redis集群

1、问题写在前面 1.1、问题描述&#xff1a;Redis集群节点地址发现失败 Unable to connect to [172.17.0.4:7303]: connection timed out: /172.17.0.4:7303 1.2、解决方案&#xff1a; redis.conf 中添加配置 cluster-announce-ip 192.168.56.11 1.3、方案出处&#xff1a;…

何在 Vue3 中使用 Cytoscape.js 创建交互式网络图

本文由ScriptEcho平台提供技术支持 项目地址&#xff1a;传送门 Cytoscape.js集成到Vue应用中 应用场景介绍 Cytoscape.js是一个用于可视化复杂网络数据的JavaScript库。它提供了丰富的功能&#xff0c;包括节点和边的创建、布局算法、交互式操作等。本文将介绍如何在Vue应…

如何将办公文档压缩成rar格式文件?

压缩包格式是我们生活工作中常用到的文件格式&#xff0c;那么如何得到一个rar格式的压缩文件&#xff1f;或者说如何将文件压缩成rar格式而不是zip格式呢&#xff1f;今天我们来了解一下如何压缩为rar格式文件。 首先&#xff0c;下载并安装WinRAR&#xff0c;然后用鼠标选择需…

Flat Ads:互动广告助力开发者高效流量变现

Flat Ads作为掌握7亿独家开发者流量的全球化营销推广平台,始终致力于为全球广告主提供高效、精准的效果营销和品牌宣传服务,同时为发布商提供高效变现、最大化收益的一站式解决方案。 Flat Ads旗下的互动广告平台作为高效能广告媒介,通过丰富的广告形式和变现场景有效地提升开…