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://xiahunao.cn/news/3018051.html

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

相关文章

Facebook革命:数字社交的全新篇章

随着互联网的不断普及和科技的飞速发展&#xff0c;社交媒体已经成为现代社会不可或缺的一部分。在众多社交媒体平台中&#xff0c;Facebook以其广泛的用户群体和强大的功能而备受瞩目。然而&#xff0c;Facebook并非止步于现状&#xff0c;而是正在掀起一场数字社交的革命&…

Github 2024-05-07 开源项目日报 Tp10

根据Github Trendings的统计,今日(2024-05-07统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量TypeScript项目4Jupyter Notebook项目2Python项目1Batchfile项目1非开发语言项目1Java项目1HTML项目1C#项目1从零开始构建你喜爱的技术 创建周期…

MySQL 高级 - 第七章 | 索引的数据结构

目录 一、为什么使用索引二、什么是索引2.1 索引的概述2.2 索引的优缺点 三、InnoDB 中索引的推演3.1 InnoDB 页简介3.2 没有索引的查找3.3 设计索引3.3.1 一个简单的索引设计方案3.3.2 InnoDB 中索引方案① 迭代 1 次&#xff1a;目录项记录的页② 迭代 2 次&#xff1a;多个目…

Java | Leetcode Java题解之第74题搜索二维矩阵

题目&#xff1a; 题解&#xff1a; class Solution {public boolean searchMatrix(int[][] matrix, int target) {int m matrix.length, n matrix[0].length;int low 0, high m * n - 1;while (low < high) {int mid (high - low) / 2 low;int x matrix[mid / n][m…

ubuntu20部署3d高斯

3d高斯的链接&#xff1a;https://github.com/graphdeco-inria/gaussian-splatting 系统环境 ubuntu20的系统环境&#xff0c;打算只运行训练的代码&#xff0c;而不去进行麻烦的可视化&#xff0c;可视化直接在windows上用他们预编译好的exe去可视化。&#xff08;因为看的很…

TC4056 1A线性锂离子电池充电器芯片IC

一、产品描述 TC4056是一款完整的单节锂离子电池采用恒定电流/恒定电压线性充电器。其底部带有散热片的ESOP8/DIP8封装与较少的外部元件数目使得TC4056成为便携式应用的理想选择。TC4056可以适合USB电源和适配器电源工作。 由于采用了内部PMOS FET架构&#xff0…

基于单片机的无线数据传输系统设计

摘要:基于单片机的无线数据传输系统的设计,实现了温度和湿度的自动采集、无线通讯和报警功能。该系统包括了LCD1602显示电路、DHT11温湿度采集电路等,完成了基于无线数据传输的方法来实现温湿度的采集。 关键词:温湿度检测;N RF 24 L 01;单片机 0 引言 随着科技水平的提高,…

DMAR: [INTR-REMAP] Present field in the IRTE entry is clear 的解决办法

问题描述 在使用FPGA开发PCIe的MSI-X中断相关功能时&#xff0c;一次测试过程中dmesg打印出如下错误&#xff0c;使用cat /proc/interrupts查看FPGA的PCIe驱动程序未收到MSIX中断。使用的系统为基于Intel x86_64的linux&#xff08;RHEL8.9&#xff09;&#xff0c;基于Xilinx …

【边东随笔】北美鳄龟的生存智慧:细心 | 信心 | 狠心 | 耐心

非常谨慎&#xff0c;在水域中会先找到躲避将自身安置于有利地形 ( 细心 &#xff09;。 浮出水面换气&#xff0c;水体稍有异动就会退回水中&#xff0c;优秀掠食者对自身优势牢牢的把握&#xff08; 信心 &#xff09;。 非常优雅&#xff0c;猎食动作不存在任何花里胡哨&a…

解决Node.js mysql客户端不支持认证协议引发的“ER_NOT_SUPPORTED_AUTH_MODE”问题

这是一个版本问题 我用koa2和mysql2链接就没有问题 不知道这个老项目运行为啥有这个问题 解决方案 打开mysql运行这个两个命令&#xff1a; ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY 123321; FLUSH PRIVILEGES; 须知(给小白看的&#xff01;) …

【Pytorch】4.torchvision.datasets的使用

什么是torchvision.datasets、 是pytorch官方给出的关于cv领域的训练数据集&#xff0c;我们可以用官方提供的数据集进行学习与训练 如何查看 我们可以进入Pytorch官网 切换一下版本到v0.9.0&#xff0c;就可以看到官方给出的数据集了 同时也有官方训练好的cv模型可以供我们…

Unity 性能优化之图片优化(八)

提示&#xff1a;仅供参考&#xff0c;有误之处&#xff0c;麻烦大佬指出&#xff0c;不胜感激&#xff01; 文章目录 前言一、可以提前和美术商量的事1.避免内存浪费&#xff08;UI图片&#xff0c;不是贴图&#xff09;2.提升图片性能 二、图片优化1.图片Max Size修改&#x…

2024年全域电商矩阵109节线上课

《24年全域电商矩阵109节线上课》是一门全面介绍电子商务领域的课程。从电子商务的基本概念到全球电子商务趋势&#xff0c;再到电子商务的营销策略和实际操作技巧&#xff0c;本课程涵盖了丰富多样的主题。学员将通过109节在线课程系统全面了解电子商务&#xff0c;并获得在这…

AI换脸免费软件Rope中文汉化蓝宝石版本全新UI界面,修复部分已知错误【附下载地址与详细使用教程】

rope蓝宝石版&#xff1a;点击下载 注意&#xff1a;此版本支持N卡、A卡、CPU&#xff0c;且建议使用中高端显卡&#xff0c;系统要求win10及以上。 Rope-蓝宝石 更新内容&#xff1a; 0214版更新&#xff1a; ①&#xff08;已修复&#xff09;恢复到以前的模型荷载参数。有…

使用 Kubeadm 搭建个公网 k8s 集群(单控制平面集群)

前言 YY&#xff1a;国庆的时候趁着阿里云和腾讯云的轻量级服务器做促销一不小心剁了个手&#x1f60e;&#x1f622;&#xff0c;2 Cores&#xff0c;4G RAM 还是阔以的&#xff0c;既然买了&#xff0c;那不能不用呀&#x1f6a9;&#xff0c;之前一直想着搭建个 k8s 集群玩…

C语言——联合体和枚举

1. 联合体 联合体和结构体类似。 联合体类型的声明&#xff1a; 联合体的特点&#xff1a; 像结构体⼀样&#xff0c;联合体也是由⼀个或者多个成员构成&#xff0c;这些成员可以是不同的类型。 但是编译器只为最⼤的成员分配⾜够的内存空间。联合体的特点是所有成员共⽤同⼀…

UDP通讯的demo

udp通讯的demo&#xff0c;这个只是简单的实现。 后面我还会加入udp组播功能。 因为懒&#xff0c;所以我自己发&#xff0c;自己接收了。 经过测试&#xff0c;可以看到&#xff0c;发送消息和接收消息功能都没问题。 广播&#xff1a; 这个是点对点的通过对方的ip和端口发…

Minio(官方docker版)容器部署时区问题研究记录

文章目录 感慨&概述补充&#xff1a;MINIO_REGION和容器时间的关系 问题一&#xff1a;minio容器和本地容器时间不一致问题说明原因探究解决方法结果验证 问题二&#xff1a;minio修改时间和本地查询结果不一致具体问题原因探究解决办法时间转化工具类调用测试和验证上传文…

数据库管理-第184期 23ai:干掉MongoDB的不一定是另一个JSON数据库(20240507)

数据库管理184期 2024-05-07 数据库管理-第184期 23ai:干掉MongoDB的不一定是另一个JSON数据库&#xff08;20240507&#xff09;1 JSON需求2 关系型表设计3 JSON关系型二元性视图3 查询视图总结 数据库管理-第184期 23ai:干掉MongoDB的不一定是另一个JSON数据库&#xff08;20…

win10下,svn上传.so文件失败

问题&#xff1a;win10下使用TortoiseSVN&#xff0c;svn上传.so文件失败 解决&#xff1a;右键&#xff0c;选择Settings&#xff0c;Global ignore pattern中删除*.so&#xff0c;保存即可。