c++笔记——概述运算符重载——解析运算符重载的难点

         前言:运算符重载是面向对象的一个重要的知识点。我们都知道内置类型可以进行一般的运算符的运算。但是如果是一个自定义类型, 这些运算符就无法使用了。那么为了解决这个问题, 我们的祖师爷就在c++中添加了运算符重载的概念。 本篇主要通过实例的实现来讲述了运算符重载的知识点。  

目录

运算符重载的应用

运算符重载的定义

赋值运算符重载

流提取和流插入

const问题

 定义位置的问题


运算符重载的应用

        首先, 先了解以下运算符重载能做些什么,先看下面这张图

        在这张图中, 红框框是我定义的一个自定义类型。 这个自定义类型的成员包括了string类型的_name。 int类型的_age.它代表的就是一个人的类型。 每一个类的实例化对象都有他们的姓名, 也有他们的年龄。 那么很显然, 它的实例化对象也可以进行年龄的加法,也就是增长年龄。 

        所以, 绿色箭头指向的位置, 如果我的本意是想要让实例化对象p1的_age加2, 也就是p1这个实例化对象的年龄增加了2。但是很显然, 这个运算对于+这个运算符来说, 是做不到的, 因为对于运算符来说, 他们默认只能处理内置类型, 而不能处理自定义类型。

        这里如果想要处理自定义类型, 就需要使用我们的运算符重载, 运算符重载的目的就是为了让一个运算符可以处理自定义类型的运算。  

        如下是我重载的peo类的一个加法运算符。

//这里我将整个类搬过来方便观察。
struct peo 
{string _name;int _age;//默认构造, 这里是默认构造, 因为每一个参数都有缺省, 那么这个就是一个默认构造函数peo(string str = string(), int age = 0) :_age(age){_name = str;}//定义的peo类的加法运算peo operator+(const int x){peo tmp = *this;tmp._age += x;return tmp;}};

        拥有了这个运算之后我们原先的代码就可以跑了:

现在是还没有执行加法运算的时候:

        下图是运算之后的p1:

        可以看到, p1的年龄从原本的18变成了20.

运算符重载的定义

        知道了运算符重载的作用,那么我们如何来重载一个运算符呢。 首先, 重载运算符的函数名统一都要使用"operator", 然后在operator后面加要重载的运算符。

        比如, 我如果想要重载一个加法运算符, 那么函数名就是这样: "operator+"

        然后就是参数的问题, c++规定, 被重载的运算符是按照操作数的前后进行传参的。 也就是说, 如果我重载了一个运算符+, 那么当我使用这个操作符的时候, “+”前面的操作符就是要传的第一个形参。 “+"后面就是要传的第二个形参。 我们利用上面的代码进行举例:

        这里需要着重注意的是第二个参数, 也就是那个const int x。 如果这里的参数, 我不使用传值接收, 而是使用传引用接收,也就是int& x, 那么就是不可以的。这里的const不能丢。 因为这里我们通常传送整形传送的都是常量,也就是右值,不能取地址的值。而如果形参引用想要接收右值有两个方法:一个是使用const 将左值引用变为常性, 那么就可以接收右值了。 一个是使用右值引用,右值引用时c++11的语法, 这个内容现在先知道有就行, 后续文章会学习到。 

        所以, 如果使用左值引用接收, 也就是我们平常使用的引用&来接收, 就需要使用const, 其实这里也时我们对于const 理解的进一步加深。 就是我们平时在使用左值引用参数接收左值时, 一定要分析好, 这个左值引用是否需要接收右值, 如果不接受右值, 那么可以不加const, 但是如果接收右值的话, 一定要加const。 而其中比较烦人的就是函数的返回值, 要知道, 函数的返回值如果是一个传值返回, 那么它在返回的时候会创建一个将亡值。 如果我们要对这个函数的返回值进行运算,或者其他需要调用函数的操作。 一定要注意,调用的这个函数是一个左值引用接收的话, 这个左值引用参数一定要加const。

        好, 这里这个板块的内容大致就这些, 我们再重载几个运算符,这个板块的内容就这么过了。


struct peo 
{string _name;int _age;peo(string str = string(), int age = 0) :_age(age){_name = str;}//加法运算符重载peo operator+(const int x){peo tmp = *this;tmp._age += x;return tmp;}//比较运算符重载bool operator>(const peo& d) {return _age > d._age;}//比较运算符重载bool operator==(const peo& d) {return _age == d._age;}//赋值运算符重载peo& operator=(const peo& d) {_name = d._name;_age = d._age;}};

赋值运算符重载

        其他的运算符都很好说, 本篇真正要着重讲解的运算符其实只有三个, 也可以说是两个, 因为有两个是差不多的。 这三个运算符就是:赋值运算符, 流提取运算符, 流插入运算符。这里流提取和流插入的性质是类似的。

       这个板块先来讲述赋值运算符。

        我的前面的文章——解析默认构造函数的那篇文章中主要分析了三个默认函数——默认构造函数,默认拷贝构造,默认析构函数。 

        这三个默认函数的性质我们再来复习一下:

  • 默认构造函数是对内置类型不做处理, 对于自定义类型会去调用它的默认构造函数。 
  • 默认拷贝构造是对内置类型就是进行浅拷贝, 对于自定义类型会去调用它的拷贝构造。 
  • 默认析构函数就是对内置类型不做处理, 对于自定义类型会去调用它的析构函数。

        但是, 其实除了上面的三个 ”我们不写,系统自动生成“ 的函数, 还有一个函数如果我们不写,系统也会自动生成。 它就是——默认赋值运算符重载。现在, 我们不写, 让编译器自己重载一个赋值运算符帮我们计算一下我们定义的peo类。

注意, 我们没有进行重载赋值符号, 我们现在通过调试观察编译器是如何帮我们进行处理的:

现在还没有调用赋值符号, 此时p1的_age成员的值是18。

        f10下一步, 执行赋值符号, 这里的p1的_age成员的值已经变了, 说明我们虽然没有自己实现赋值运算符的重载, 但是编译器自己帮我们生成了。 

        编译器默认生成的赋值运算符重载的性质:编译器默认生成的赋值重载对于内置类型进行浅拷贝, 对于自定义类型会去调用它的赋值重载。

        其实, 对于默认赋值运算符重载我们可以这么理解:


class A 
{
public:int a;int b;//......其他成员变量A(const A& ka) {//a = ka.a;//b = ka.b;//....其他成员变量使用赋值运算符进行赋值}};

        就是类似于上图, 其实编译器默认生成的赋值运算符重载我们可以理解为:对象的每一个成员变量都是用赋值符号进行直接赋值操作。 那么对于内置类型就是普通的浅拷贝。 对于自定义类型就会去调用这个自定义类型的赋值运算符重载。 

        对于赋值重载来说, 其实最重要的就是默认赋值重载。而赋值重载的重载方式与其他的重载符号没有什么不同。

流提取和流插入

const问题

        我们平时使用的>>就是库里面重载的一个流提取运算符。对于istream,由于知识储备不足, 博主不能给出详细的解释。 我们在这个板块只讨论很浅的知识点。我们看下面的一段代码:

#include<iostream>
using namespace std;
#include<assert.h>struct student 
{char _name[20];int _age;char _gender[10];int _num;};const istream& operator>>(const istream& in, student& stu) 
{cout << "请输入姓名, 年龄, 性别, 和学号:>";in >> stu._name >> stu._age >> stu._gender >> stu._num;return in;
}

        这一串代码中的流提取是有问题的。 为什么? 

        这里我们可以这么理解: const 修饰istream类型的对象 in,对象in具有常性。然后在istream类里面的流指针成员变量(这里可以想象成有一个流指针成员变量)也具有了常性。 那么, 如果类被赋予了常性, 成员指针变成什么?其实是变成指针常量, 也就是不能修改指向。 所以, 如果istream类型的in具有常性, 那么它里面的指针成员就会都变成指针常量。 流指针无法在终端一个字符一个字符的改变读取数据。所以图中的流提取是不对的。

        流插入也是同样的道理, 如果给ostream的实例化对象赋予常性, 那么里面的流指针就无法改变指向, 那么就无法依次在终端输入数据。 所以, 对于流插入和流提取的运算符, 最重要的一点就是不能给他们加上const。 否则这里就无法正常编译。 正确的重载如下:

#include<iostream>
using namespace std;
#include<assert.h>struct student 
{char _name[20];int _age;char _gender[10];int _num;};istream& operator>>(istream& in, student& stu) 
{cout << "请输入姓名, 年龄, 性别, 和学号:>";in >> stu._name >> stu._age >> stu._gender >> stu._num;return in;
}ostream& operator<<(ostream& out, const student& stu) 
{out << stu._name << " " << stu._age << " " << stu._gender << " " << stu._num << endl;;return out;
}

 定义位置的问题

        然后, 对于流插入和流提取还有一个比较重要的问题就是它的定义的位置。 我们知道, 对于一个类来说, 它里面的不管是非静态成员函数还是运算符重载的第一个参数都默认是this指针。但是, 要知道, 我们在进行流插入或者流提取操作时, 谁在前, 谁在后?(就像 cin >> a)

        很明显, 流插入或者流提取符号在前, 然后标识符在后。 所以, 我们在重载流插入和流提取操作符的时候就要让第一个参数是istream或者ostream。 但是, 如果我们将这两种重载运算符放在类域中。那么第一个参数就会变成this指针, 这个就无法满足流插入流提取运算符重载的规则了。 就不对了。 所以, 我们应该将流插入运算符或者流提取运算符的定义放在类域外。 

        就像上面的一串代码, 这里搬一下放在下面:

        就像如图, 这里我定义的流提取和流插入就是在类域外。然后, 第一个参数是out, 第二个参数才是学生类。 如果不定义在类外, 那么第一个参数默认为学生类。 就无法满足要求。

----------------------------------------------------------------------------------------------------------------------

以上, 就是本节的全部内容。

        

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

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

相关文章

网络网络层之(4)IPv4协议

网络网络层之(1)IPv4协议 Author: Once Day Date: 2024年4月4日 一位热衷于Linux学习和开发的菜鸟&#xff0c;试图谱写一场冒险之旅&#xff0c;也许终点只是一场白日梦… 漫漫长路&#xff0c;有人对你微笑过嘛… 全系列文档可参考专栏&#xff1a;通信网络技术_Once-Day的…

数据结构-线性表-应用题-2.2-8

已知在一维数组A[mn]中依次存放两个线性表&#xff08;a1,a2,a3,...am&#xff09;和&#xff08;b1,b2,b3,...bm&#xff09;编写一个函数&#xff0c;将数组中的两个顺序表的位置互换&#xff0c;即将&#xff08;b1,b2,b3,...bm&#xff09;放在&#xff08;a1,a2,a3,...am&…

python 关键字(in)

9、in 在Python中&#xff0c;in关键字是一个强大的工具&#xff0c;用于检查一个元素是否存在于某个序列&#xff08;如列表、元组、字符串等&#xff09;或集合&#xff08;如集合、字典的键&#xff09;中。 基础小白知识&#xff1a;in的基本用法 1.1 在序列中检查元素 …

JAVASE复习之多线程

多线程 1 多线程入门1.1 多线程的创建方式1.1.1 继承Thread方式1.1.2 实现Runable方式 1.2 Thread类中常用方法 1 多线程入门 1.1 多线程的创建方式 1.1.1 继承Thread方式 基本步骤&#xff1a; 创建一个类继承Thread类。在类中重写run方法&#xff08;线程执行的任务放在这…

《QT实用小工具·五十九》随机图形验证码,带有一些可人的交互与动画

1、概述 源码放在文章末尾 该项目实现了可交互的动画验证码控件&#xff0c;趣味性十足&#xff1a; 字符变换动画 噪音动画 可拖动交互 项目demo演示如下所示&#xff1a; 项目部分代码如下所示&#xff1a; #ifndef CAPTCHAMOVABLELABEL_H #define CAPTCHAMOVABLELABEL…

线程池前世今生及源码实现

文章目录 ⭐前序一、是什么(what)功能构成 二、为什么(why)三、何处(where)四、何时(when)五、谁(who)六、怎么样(how)实践和性能调优策略线程池源码&#xff08;Code&#xff09;[待补充] ⭐前序 本文讲什么&#xff1a; 线程池的概念、工作原理、优势、实际应用中的使用场景…

VMware虚拟机提示内存不足

VMware虚拟机&#xff0c;k8s集群搭建内存不足的问题 疑问&#xff1a;我的电脑是8G8G双通道的内存&#xff0c;当我在搭建k8s集群时给master-2G内存&#xff0c;node1-3G内存&#xff0c;node2-3G内存&#xff1b; 当依次打开虚拟机到node2时VM提示“物理内存不足&#xff0c;…

【win32_004】系统配置信息、HIWORD宏、打印系统配置信息

文章目录 1. 获取系统度量或系统配置信息2. 格式化文本输出到图形输出3. HIWORD宏 1. 获取系统度量或系统配置信息 函数介绍 int WINAPI GetSystemMetrics(_In_ int nIndex);2. 格式化文本输出到图形输出 #mermaid-svg-RJl99A670ndu7R9k {font-family:"trebuchet ms&quo…

【busybox记录】【shell指令】cut

目录 内容来源&#xff1a; 【GUN】【cut】指令介绍 【busybox】【cut】指令介绍 【linux】【cut】指令介绍 使用示例&#xff1a; 关于参数的特殊说明&#xff1a; 打印行中选定部分 - 输出每行的第n-m个字节 打印行中选定部分 - 输出每行的第n-m个字符 打印行中选定…

【论文阅读】Fuzz4All: Universal Fuzzing with Large Language Models

文章目录 摘要一、介绍二、Fuzz4All的方法2.1、自动提示2.1.1、自动提示算法2.1.2、自动提示的例子2.1.3、与现有自动提示技术的比较 2.2、fuzzing循环2.2.1、模糊循环算法2.2.2、Oracle 三、实验设计3.1、实现3.2、被测系统和baseline3.3、实验设置以及评估指标 四、结果分析4…

每日OJ题_贪心算法三③_力扣45. 跳跃游戏 II(dp解法+贪心解法)

目录 力扣45. 跳跃游戏 II 解析代码1_动态规划 解析代码2_贪心 力扣45. 跳跃游戏 II 45. 跳跃游戏 II 难度 中等 给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。 每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说&#xff0c;如果你在 num…

Windows在命令行控制台 CMD DOS PowerShell 创建文件夹 md mkdir New-Item

Windows在命令行控制台 CMD DOS PowerShell 创建文件夹 DOS可用mkdir命令创建文件夹CMD可用md命令创建文件夹PowerShell可用New-Item命令创建文件夹 测试代码 ### 在PowerShell中用井号 # 来单行注释, <#多行注释#> 不能用cmd中的 :: , rem空格 # PowerShell兼容 Dos…

ws注入js逆向调用函数

这里需要选择一个文件夹 随便 紫色为修改保存 记得ctrls保存 注入代码如下 (function() {var ws new WebSocket("ws://127.0.0.1:8080")ws.onmessage function(evt) {console.log("收到消息&#xff1a;" evt.data);if (evt.data "exit") {…

[3D基础]Delaunay与Triangulate构网

Delaunay三角剖分&#xff08;Delaunay Triangulation&#xff09;和Triangulate构网是两种常用于生成三角网格的方法&#xff0c;它们都有其独特的特点和应用场景。 Delaunay三角剖分&#xff1a; Delaunay三角剖分是一种经典的三角剖分方法&#xff0c;其特点是任意三角形的外…

PHPStudy Apache或者MySQL启动以后自动停止

问题 phpstudy小皮面板中的Apache或MySQL启动以后自动停止 正在启动——已启动——已停止 总结&#xff1a;最主要的原因&#xff1a;端口冲突 端口冲突了&#xff0c;已经有其他程序占用了80、3306端口。 也就是说你的电脑上已经有了一个Apache、MySQL并且正在运行。 解决方案…

C++ list 介绍

&#x1f308;一、认识list这个模版 ist是一个模版&#xff0c;需要结合一个具体的数据类型作为模版参数&#xff0c; 即list < T > <T> <T>&#xff0c;才能成为一个类类型。list是双向循环链表&#xff0c;是序列容器&#xff0c;允许在序列中的任何位置进…

前端技术Stylus详解与引入

Stylus 是一种动态样式语言&#xff0c;它允许使用更少的代码来生成 CSS。它是一个预处理器&#xff0c;这意味着你需要在浏览器加载前将 Stylus 代码转换成 CSS。Stylus 提供了多种功能&#xff0c;如变量、混合&#xff08;mixins&#xff09;、函数、继承等&#xff0c;这些…

DNS域名解析服务的部署及优化方案

实验要求: 1.配置2台服务器要求如下&#xff1a; a&#xff09;服务器1&#xff1a; 主机名&#xff1a;dns-master.timinglee.org ip地址&#xff1a; 172.25.254.100 配置好软件仓库 b&#xff09;服务器2&#xff1a; 主机名&#xff1a;dns-slave.timinglee.org ip地址&am…

MySQL数据库练习——视图

schooldb库——utf8字符集——utf8_general_ci排序规则 先创建库&#xff0c;再去使用下列的DDL语句。 DDL CREATE TABLE student (id int(11) NOT NULL AUTO_INCREMENT COMMENT 学号,createDate datetime DEFAULT NULL COMMENT 创建时间,modifyDate datetime DEFAULT NULL …

使用 GPT-4-turbo+Streamlit+wiki+calculator构建Math Agents应用【Step by Step】

&#x1f496; Brief&#xff1a;大家好&#xff0c;我是Zeeland。Tags: 大模型创业、LangChain Top Contributor、算法工程师、Promptulate founder、Python开发者。&#x1f4dd; CSDN主页&#xff1a;Zeeland&#x1f525;&#x1f4e3; 个人说明书&#xff1a;Zeeland&…