【C++】运算符重载 | 赋值运算符重载

Ⅰ. 运算符重载

引入

❓什么叫运算符重载?

就是:运用函数,将现有的运算符重新定义,使其能满足各种自定义类型的运算。

回想一下,我们以前运算的对象是不是都是int、char这种内置类型?

那我们自定义的“preson”类型,想要进行加减运算,该怎么办呢?

这就需要运算符重载。

概念

运算符重载是具有特殊函数名的函数

也具有其返回值类型、函数名及参数列表。

函数名:关键字operator后面接需要重载的运算符符号。

格式:返回值类型 operator 操作符(参数列表)

1.常用的操作符有:+、-、*、/、++、--、=(赋值)、==(判断相等)、>、<、>=、<=等

2.有几个操作数,就有几个形参。

不过,当重载成员函数时,有一个形参是隐形的,即this指针。

说明:

1.不能通过连接其他符号来创建新的操作符。

如:operator@

2.重载操作符必须有一个类类型的参数。

如果参数里没有类类型,那运算符重载还有啥意义。

3.用于内置类型的运算符,其含义不能改变。

如:内置的 整型 +,不能改变其含义

4.作为类成员函数重载时,其形参看起来比操作数少1

因为成员函数的第一个参数为隐藏的this

(见下面的例子)

5.(笔试选择题常考)这5个运算符不能重载:

.* 点星运算符

: : 域运算符

sizeof

? : 条件运算符

. 点运算符

6.运算符重载写好了以后,直接用就行。编译器会自动调用函数。

Date& operator+=(int day){

}

d1+=100;    //直接用。调用会自动完成🤣

举例

class Date
{
public:
    Date(int year = 2000, int month = 1, int day = 1)
    {
        _year = year;
        _month = month;
        _day = day;
    }                                 //需要注意的是,左操作数是this,指向调用函数的对象
    bool operator==(const Date& d)    //bool operator==(Date*this,const Date& d)
    {
        return _year == d._year &&    //其实是this->_year==d._year
            _month == d._month &&
            _day == d._day;
    }
​
private:
    int _year;
    int _month;
    int _day;
};
int main()
{
    Date d1(2023, 8, 12);
    Date d2(2023, 8, 12);
    cout << (d2 == d1) << endl;
}

结果为:

 

 

Ⅱ. 赋值运算符重载

概念

赋值运算符重载作为类的6大成员函数之一,

负责将一个对象赋值给另一个对象。

如果我们不写,那编译器会自动生成

格式

T& operator =(const T& 参数)

 

说明:

1.参数类型为const T&。传引用可以提高传参效率。

2.返回类型为T&。

❓你可能会疑惑:这里只要完成赋值动作的话,返回类型为void不就可以了吗?

为什么要有返回值呢?

有返回值其实是为了支持连续赋值。

如”d1=d2=d3;“ 要想连续赋值,

那d2=d3在调用完函数以后要有一个返回值,这个返回值作为右操作数,参与到d1=…中去。

如果返回void,那d1=空,无法完成连续赋值。

所以,要想连续赋值,就得有返回值。

在返回时,我们尽量使用引用返回

因为能减少传参过程中的拷贝,效率更高。

不信我们来实验下,

通过对比 传值返回与传引用 调用拷贝构造函数 的次数,

来看 传引用究竟有没有减少拷贝!

实验组1:传值

class Date
{
public:
    Date(int year = 2000, int month = 1, int day = 1)
    {
        _year = year;
        _month = month;
        _day = day;
    }
    Date(const Date& d)    //我们自己写一个拷贝构造函数
    {                      //这样,它每次被调用,都会打印出来
        cout << "我被调用了!" << endl;
        _year = d._year;
        _month = d._month;
        _day = d._day;
    }
    Date operator=(const Date& d)   //传值是可以的,但是没有引用好
    {                                //实验结果将为我们证明这一点
        _year = d._year;
        _month = d._month;
        _day = d._day;
        return *this;
    }
private:
    int _year;
    int _month;
    int _day;
};
int main()
{
    Date d1(2023, 8, 12);
    Date d2(2023, 8, 12);
    Date d3(2000, 1, 1);
    Date d4(2020, 1, 1);
    d1 = d2 =d3 = d4;
}

结果为:(这已经是被优化后的结果)

 

 

实验组2:传引用

...
    Date& operator=(const Date& d)   //传引用
    {
        _year = d._year;
        _month = d._month;
        _day = d._day;
        return *this;
    }
    ...

结果为:

 

 

实验证明,传引用比传值调用拷贝构造函数的次数少,效率更高。

所以,我们能传引用的地方,就尽量传引用。

3.检测是否自己给自己赋值。

如果是”d1=d1;“那这样的赋值完全没意义。

为了更高效,我们用if语句来避免自己给自己赋值的情况。

Date& operator=(const Date& d)
    {
        if (this != &d)    //加上判断
        {
            _year = d._year;
            _month = d._month;
            _day = d._day;
            return *this;
        }
    }

注:我们要用this去判断,不要用对象!

因为对象仅能判断值是否相等,而this能从地址判断它俩是否是同一个。

4.返回*this。

为什么可以返回*this?

我们知道,函数的局部变量是不能返回的,

因为局部变量在出了作用域就销毁了。

而这里不同,*this是 作用域在函数外面的 对象。

出作用域,对象并不会因此销毁。所以*this有效。

只能重载成成员函数

赋值运算符只能重载成成员函数,不能重载成全局函数。😥

因为如果不在类中实现,那编译器会生成一个默认的。

此时你在类外实现的全局运算符重载,就和默认的那个冲突了。

因此,赋值运算符必须定义成成员函数。

赋值or拷贝构造?

来看这个例子:这两种写法,分别是赋值还是拷贝构造?

  

其实都是拷贝构造!

赋值操作的是一个已存在的变量👌,而拷贝构造是定义一个新的变量。

默认赋值运算符重载

当你没有显示实现时,编译器会自动生成一个默认的赋值运算符重载,

以值的方式逐字节拷贝。

注:内置类型成员是直接赋值的,

而自定义类型成员变量需要调用 对应类的 赋值运算符重载 来完成赋值。

我们演示一下:

class Date {
public:
    Date(int year = 2000, int month = 1, int day = 1)
    {
        this->_year = year;
        this->_month = month;
        this->_day = day;
    }
private:
    int _year;
    int _month;
    int _day;
};
​
int main(void)
{
    Date d1(2010,1,1);
    Date d2(2023,8,12);
                  //我们并未实现,
    d1 = d2;   //但这里会自动调用 默认赋值运算符重载
    return 0;
}

结果:

 

❓既然默认生成的已经可以完成值的拷贝了,那还需要我们自己去实现吗?

如果是像日期类这种,是不需要的,值拷贝已经足够。

但如果涉及资源管理,

如动态内存分配、指针、打开的文件等,就得深拷贝,

这时就必须要自己去实现了。

(这里的原因和拷贝构造函数那儿是贯通的。)

原因再说明一下:

如果有指针,而默认的赋值运算符重载只能浅拷贝,

并不会再开一块指针指向的空间。

这就导致了两个指针指向同一块空间,彼此相互影响。

 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

CST HFSS MATLAB参数方程定义曲面绘制

CST HFSS 函数定义曲面绘制 简介环境HFSSCSTMATLAB 简介 若在柱坐标系中半径r随z和phi都会变&#xff0c;无法使用一般的方法绘制&#xff0c;这时可以使用参数方程定义的曲面来绘制。举一个例子如下&#xff0c; r 100 0.5 ( c o s ( 0.2 ∗ p i ∗ z ) − 1 ) c o s ( φ …

Ganache 本地测试网远程连接

文章目录 前言1. 安装Ganache2. 安装cpolar3. 创建公网地址4. 公网访问连接5. 固定公网地址 前言 Ganache 是DApp的测试网络&#xff0c;提供图形化界面&#xff0c;log日志等&#xff1b;智能合约部署时需要连接测试网络。 Ganache 是一个运行在本地测试的网络,通过结合cpol…

哪些人适合参加大数据培训班?

互联网加速职场变革&#xff0c;大数据浪潮席卷全球。日前&#xff0c;Python、大数据、人工智能是当今最热门的话题。大数据存储、大数据分析、 人工智能等开发人才需求旺盛。 大数据培训班有大数据分析培训班、大数据开发培训班&#xff0c;JAVA培训班 大数据班适学人群…

【RabbitMQ】RabbitMQ整合SpringBoot案例

文章目录 1、前情提要【RabbitMQ】2、RabbitMQ-SpringBoot案例 -fanout模式2.1 实现架构总览2.2 具体实现2.2.1生产者2.2.1消费者 1、前情提要【RabbitMQ】 【RabbitMQ】消息队列-RabbitMQ篇章 RabbitMQ实现流程 2、RabbitMQ-SpringBoot案例 -fanout模式 2.1 实现架构总览…

快速学习GO语言总结

备注&#xff1a;本博客将自己初步学习GO的总结进行分享&#xff0c;希望大家通过本博客可以在短时间内快速掌握GO的基本程序编码能力&#xff0c;如有错误请留言指正&#xff0c;谢谢&#xff01; 一、初步了解Go语言 &#xff08;一&#xff09;Go语言诞生的主要问题和目标…

Elasticsearch:语义搜索 - Semantic Search in python

当 OpenAI 于 2022 年 11 月发布 ChatGPT 时&#xff0c;引发了人们对人工智能和机器学习的新一波兴趣。 尽管必要的技术创新已经出现了近十年&#xff0c;而且基本原理的历史甚至更早&#xff0c;但这种巨大的转变引发了各种发展的“寒武纪大爆炸”&#xff0c;特别是在大型语…

【数据结构】_7.二叉树

目录 1.树形结构 1.1 树的概念 1.2 树的相关概念 1.3 树的表示 1.4 树在实际中的应用—表示文件系统的目录树结构 ​编辑​2.二叉树 2.1 概念 2.2 特殊二叉树 2.3 二叉树的性质 2.4 二叉树的存储结构 2.4.1 顺序存储结构&#xff08;数组存储结构&#xff09; 2.4.2…

7个改变玩法规则的ChatGPT应用场景

ChatGPT因各种原因受到了广泛关注&#xff1a;ChatGPT可以充当各种改善生活改进工作的小助手&#xff0c;如内容写手、客户支持、语言翻译、编码专家等等。只需在你的聊天内容中添加适当的提示&#xff0c;人工智能将为你提供各项支持。[1] 1.ChatGPT作为内容写手 通过AI的帮助…

flink jira 提交开源bug

注册apache issue账号,并申请flink空间的权限后. 提问题/bug 查看已经提交的问题:

PHP“牵手”淘宝商品评论数据采集方法,淘宝API接口申请指南

淘宝天猫商品评论数据接口 API 是开放平台提供的一种 API 接口&#xff0c;它可以帮助开发者获取商品的详细信息&#xff0c;包括商品的标题、描述、图片等信息。在电商平台的开发中&#xff0c;详情接口API是非常常用的 API&#xff0c;因此本文将详细介绍详情接口 API 的使用…

SRM系统询价竞价管理:优化采购流程的全面解析

SRM系统的询价竞价管理模块是现代企业采购管理中的重要工具。通过该模块&#xff0c;企业可以实现供应商的询价、竞价和合同管理等关键环节的自动化和优化。 一、概述 SRM系统是一种用于管理和优化供应商关系的软件系统。它通过集成各个环节&#xff0c;包括供应商信息管理、询…

龙讯旷腾PWmat已部署至曙光智算平台

编者荐语&#xff1a; 近期&#xff0c;龙讯旷腾核心产品PWmat已成功部署至曙光智算AC.sugon.com平台&#xff0c;可为用户提供包括分子建模、第一性原理计算、数据可视化等在内的完备的超级计算云服务&#xff0c;让大家能够轻松上手具有完全自主知识产权的大尺度高性能材料计…

游戏找不到msvcr100.dll解决方法,常见的三种解决方法

在计算机领域&#xff0c;msvcr100.dll是一个非常重要的动态链接库文件。它是Microsoft Visual C 2010 Redistributable的一部分&#xff0c;用于支持Visual Studio 2010的开发环境。然而&#xff0c;在某些情况下&#xff0c;msvcr100.dll可能会出现问题&#xff0c;导致程序无…

什么是大数据测试?有哪些类型?应该怎么测?

随着目前世界上各个国家使用大数据应用程序或应用大数据技术场景的数量呈指数增长&#xff0c;相应的&#xff0c;对于测试大数据应用时所需的知识与大数据测试工程师的需求也在同步增加。 针对大数据测试的相关技术已慢慢成为当下软件测试人员需要了解和掌握的一门通用技术。…

redis7高级篇2 redis的BigKey的处理

一 Bigkey的处理 1.1 模拟造数 1.截图 2.代码 &#xff1a;使用pipe 批量插入10w的数据量 cat /root/export/monidata.txt | redis-cli -h 127.0.0.1 -a 123456 -p 6379 --pipe [rootlocalhost export]# for((i1;i<10*10;i)); do echo "set k$i v$i" >>…

Java之包,权限修饰符,final关键字详解

包 2.1 包 包在操作系统中其实就是一个文件夹。包是用来分门别类的管理技术&#xff0c;不同的技术类放在不同的包下&#xff0c;方便管理和维护。 在IDEA项目中&#xff0c;建包的操作如下&#xff1a; 包名的命名规范&#xff1a; 路径名.路径名.xxx.xxx // 例如&#xff…

STM32 GPIO复习

GPIO General Purpose Input Output&#xff0c;即通用输入输出端口&#xff0c;简称GPIO。 负责采集外部器件的信息或控制外部器件工作&#xff0c;即输入输出。 不同型号&#xff0c;IO口数量可能不一样&#xff0c;可通过选型手册快速查询。 能快速翻转&#xff0c;每次翻…

allegro env 文件路径

很多人说在cadence安装路径里修改env文件不生效&#xff0c;或者在安装目录里找不到env文件路径。 原因可能是 用户环境变量中的HOME路径修改导致的&#xff0c;allegro会抓取HOME变量定义的路径中的env文件。所以你如果要修改env文件&#xff0c;最好看看HOME路径&#xff0c…

C++11 新特性 ---- noexcept

1. 异常 异常通常用于处理逻辑上可能发生的错误 在C98中&#xff0c;提供了一套完善的异常处理机制&#xff0c;直接在程序中将各种类型的异常抛出&#xff0c;从而强制终止程序的运行。 1.1 基本语法 当函数抛出异常时&#xff0c;程序会停止执行&#xff0c;并显示异常信息…

接口性能测试 —— Jmeter并发与持续性压测

接口压测的方式&#xff1a; 1、同时并发&#xff1a;设置线程组、执行时间、循环次数&#xff0c;这种方式可以控制接口请求的次数 2、持续压测&#xff1a;设置线程组、循环次数&#xff0c;勾选“永远”&#xff0c;调度器&#xff08;持续时间&#xff09;&#xff0c;这种…