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

😁博客主页😁:🚀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.mzph.cn/bicheng/31693.shtml

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

相关文章

银河麒麟V10安装docker和docker-compose

1. 说明 系统镜像使用的是Kylin-Server-V10-SP3-2403-Release-20240426-x86_64.iso如果是在VMware中安装这个系统&#xff0c;需选择Ubuntu&#xff0c;如果选Centos会有问题。 尝试使用在线方式安装docker&#xff0c;报了很多错误&#xff0c;比较麻烦&#xff0c;建议使用离…

xss初识(xss-lab)

XSS跨站脚本 XSS漏洞概述 XSS被称为跨站脚本攻击&#xff08;Cross-site scripting&#xff09;&#xff0c;由于和CSS&#xff08;Cascading Style Sheets&#xff09; 重名&#xff0c;所以改为XSS。 XSS主要基于javascript语言完成恶意的攻击行为&#xff0c;因为javascri…

前端代码打包教程

一、 首先解压并进入源码包中&#xff0c;源码文件是下载的完整版安装包的 view/ 目录下,平台后台是 admin.zip ,商户后台是 mer.zip , H5/公众号/小程序是 uniapp.zip ,2.0级以上版本还有客服的源码包&#xff1b; 二、 打包&#xff0c;移动端和后台的打包方式不同 平台后台…

echarts+vue2实战(一)

目录 一、项目准备 二、(横向分页)柱状图 2.1、动态刷新 2.2、UI调整 2.3、分辨率适配 三、(竖向平移)柱状图 3.1、平移动画 3.2、不同数值显示不同颜色 四、(下拉切换)折线图 4.1、切换图表和分辨率适配 4.2、UI调整 五、(三级分类)饼图 5.1、数据切换 六、圆环…

使用Tkinter创建带查找功能的文本编辑器

使用Tkinter创建带查找功能的文本编辑器 介绍效果代码解析创建主窗口添加菜单栏实现文件操作实现查找 完整代码 介绍 在这篇博客中&#xff0c;我将分享如何使用Python的Tkinter库创建一个带有查找功能的简单文本编辑器。 效果 代码解析 创建主窗口 import tkinter as tkcl…

Offset Explorer 连接SASL PLAIN鉴权的Kafka

1、填写Kafka信息 2、配置鉴权信息 Security 选择 SASL PLAINTEXT JAAS Config 配置账号密码 org.apache.kafka.common.security.plain.PlainLoginModule required username"账号"password"密码";

[Vulnhub] Troll FTP匿名登录+定时任务权限提升

信息收集 IP AddressPorts Opening192.168.8.104TCP:21,22,80 $ nmap -sC -sV 192.168.8.104 -p- --min-rate 1000 Nmap scan report for 192.168.8.104 (192.168.8.104) Host is up (0.0042s latency). Not shown: 65532 closed tcp ports (conn-refused) PORT STATE SER…

openh264 宏块级码率控制源码分析

openh264 宏块级码率控制函数关系 宏块级核心函数分析 WelsRcMbInitGom函数 功能&#xff1a;openh264 码率控制框架中宏块级码率控制函数&#xff0c;根据是否启用GOM QP来决定如何设置宏块的QP值&#xff0c;以控制编码的质量和比特率。原理过程&#xff1a; 函数参数&…

“打造智能售货机系统,基于ruoyi微服务版本开源项目“

目录 # 开篇 售货机术语 1. 表设计说明 2. 页面展示 2.1 区域管理页面 2.2 合作商管理页面 2.3 点位管理页面 3. 建表资源 3.1 创建表的 SQL 语句&#xff08;包含字段备注&#xff09; 1. Region 表 2. Node 表 3. Partner 表 4. 创建 tb_vending_machine 表的 S…

【启明智显产品介绍】Model3C工业级HMI芯片详解专题(一)芯片性能

【启明智显产品介绍】工业级HMI芯片Model3C详解&#xff08;一&#xff09;芯片性能 Model3C 是一款基于 RISC-V 的高性能、国产自主、工业级高清显示与智能控制 MCU&#xff0c;配置平头哥E907&#xff0c;主频400MHz&#xff0c;强大的 2D 图形加速处理器、PNG/JPEG 解码引擎…

【Conda】修改 Conda 默认的虚拟环境位置

文章目录 问题描述分析与解决查看默认安装位置修改 .condarc 文件修改权限 参考资料 问题描述 Conda 的虚拟环境默认安装在 C 盘。时间久了&#xff0c;C 盘上的内存会被大量占用&#xff0c;影响电脑性能。于是想到修改虚拟环境的默认存放位置&#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;或你在工作的时候安装…

分享HTML显示2D/3D时间

效果截图 实现代码 <!DOCTYPE html> <head> <title>three.jscannon.js Web 3D</title><meta charset"utf-8"><meta name"viewport" content"widthdevice-width,initial-scale1,maximum-scale1"><meta n…

图神经网络学习笔记

文章目录 一、图神经网络应用领域分析二、图基本模块定义三、邻接矩阵的定义四、GNN中常见任务五、消息传递计算方法六、多层GCN的作用七、GCN基本模型概述八、图卷积的基本计算方法九、邻接的矩阵的变换十、GCN变换原理解读 本笔记参考自b站up主小巴只爱学习的图神经网络教程 …

创建OpenWRT虚拟机

环境&#xff1a;Ubuntu 2204&#xff0c;VM VirtualBox 7.0.18 安装必备软件包&#xff1a; sudo apt update sudo apt install subversion automake make cmake uuid-dev gcc vim build-essential clang flex bison g gawk gcc-multilib g-multilib gettext git libncurses…

C语言中操作符详解(一)

众所周知&#xff0c;在我们的C语言中有着各式各样的操作符&#xff0c;并且在此之前呢&#xff0c;我们已经认识并运用了许许多多的操作符&#xff0c;都是诸君的老朋友了昂 操作符作为我们使用C语言的一个非常非常非常重要的工具&#xff0c;诸君一定要加以重视&#xff0c;…

大模型如何改变世界?李彦宏:未来至少一半人要学会“提问题“

2023年爆火的大模型&#xff0c;对我们来说意味着什么&#xff1f; 百度创始人、董事长兼CEO李彦宏认为&#xff0c;“大模型即将改变世界。” 5月26日&#xff0c;李彦宏参加了在北京举办的2023中关村论坛&#xff0c;发表了题为《大模型改变世界》的演讲。李彦宏认为&#…

为什么我在 PostgreSQL 中 Commit 很慢?

有时&#xff0c;我们的一位客户会查看数据库中最耗时的语句&#xff08;使用pg_stat_statements或pgBadger&#xff09;&#xff0c;并发现COMMIT排名靠前。通常&#xff0c;COMMIT这是 PostgreSQL 中非常快的语句&#xff0c;因此值得研究。在本文中&#xff0c;我将探讨速度…

Go微服务: redis分布式锁在集群中可能遇到的问题及其解决方案

概述 我们的 redis 一般都是集群来给我们程序提供服务的&#xff0c;单体的redis现在也不多见 看到上面是主节点redis和下面是6个重节点redis&#xff0c;主节点和重节点的通讯都是畅通没问题的这个时候&#xff0c;我们有 gorouting 写我们的数据&#xff0c;那它就会用到我们…

(Python)可变类型不可变类型;引用传递值传递;浅拷贝深拷贝

从一段代码开始说事&#xff0c;先上代码&#xff1a; a [[1],[2],[3]] b [[4,5],[6,7],[7,8]] for i,j in zip(a,b):print(i,j)i [9]#i[0] 8j[:2][1,2]print(i, j) print(a) print(b) 运行的结果&#xff1a; [1] [4, 5] [9] [1, 2] [2] [6, 7] [9] [1, 2] [3] [7, 8] …