【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;建议使用离…

slam移动机器人预测n秒后的里程数据

slam移动机器人预测n秒后的里程数据 为了实现这个功能,需要完成以下几个步骤: 订阅/odom话题并获取当前和上一时刻的里程计数据。计算两次里程计数据之间的位置和角度的偏移量。计算时间间隔dt。使用运动模型计算当前的速度vx, vy, vth。预测3秒后的位置和角度。将预测的位置…

C/C++ 简单的输入输出测试用例

在实际开发中经常会遇到简单调试接口的场景,需要从键盘输入一个字符,响应相应操作,整体逻辑就是 1.获取键盘输入 2.执行操作 3.再次等待获取键盘输入 虽然上述步骤极其容易但是总归是需要时间去拉一个demo,下面给出demo 用例,便于大家修改测试 #include <iostream>…

Flask中获取HTTPS请求的代理IP的原始IP地址

在Flask中获取HTTPS请求的代理IP的原始IP地址&#xff0c;你可以使用X-Forwarded-For HTTP头。这个头可能包含多个IP地址&#xff0c;其中第一个通常是原始的客户端IP。 以下是一个Flask示例代码&#xff0c;展示了如何获取原始的客户端IP地址&#xff1a; from flask import…

生成式AI和LLM如何应用

承认自己是普通人&#xff0c;我们只要会用就行了。 没有Machine Learning&#xff0c;也没有Deep Learning&#xff0c;让95%的普通码农都会用大模型&#xff0c;至于底层原理&#xff0c;就让那5%的精英去学吧。 这是“Generative AI with Large Language Models”课程的学…

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;移动端和后台的打包方式不同 平台后台…

PyTorch(一)模型训练过程

PyTorch&#xff08;一&#xff09;模型训练过程 #c 总结 实践总结 该实践从「数据处理」开始到最后利用训练好的「模型」预测&#xff0c;感受到了整个模型的训练过程。其中也有部分知识点&#xff0c;例如定义神经网络&#xff0c;只是初步的模仿&#xff0c;有一个比较浅的…

力扣456.132模式

力扣456.132模式 单调栈 维护单调递减的栈用k维护‘2’&#xff0c;每次出栈就更新**&#xff08;保证栈中元素始终大于k&#xff09;**当出现nums[i] < k时 说明存在‘1’又因为栈中存在‘3’因此就存在132模式序列 class Solution {public:bool find132pattern(vector&…

C语言C99标准、C11标准新增加的特性

C语言标准 C语言从其诞生至今&#xff0c;经历了多个标准的更新&#xff0c;主要标准包括&#xff1a; C89/C90 (ANSI C / ISO/IEC 9899:1990)&#xff1a;这是C语言的第一个官方标准&#xff0c;由ANSI于1989年发布&#xff0c;后被ISO采纳为国际标准&#xff0c;发布于1990年…

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…

学习java第一百零六天

Spring的后置处理器 BeanPostProcessor&#xff1a;Bean的后置处理器&#xff0c;主要在bean初始化前后工作。 InstantiationAwareBeanPostProcessor&#xff1a;继承于BeanPostProcessor&#xff0c;主要在实例化bean前后工作&#xff1b; AOP创建代理对象就是通过该接口实现…

用LangChain调用Ollama的时候一个小问题

说来让人无语&#xff0c;简单记录一下。安装好Ollama后&#xff0c;我们通常通过访问http://127.0.0.1:11434来测试其是否正常&#xff0c;通常会出来“Ollama is running”&#xff0c;然后我习惯性地从Chrome把地址拷贝到VS Code&#xff0c; oembed OllamaEmbeddings(bas…

【启明智显产品介绍】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;改到自定义的位置。…