【c++】——类和对象(中)——赋值运算符重载

作者:chlorine

专栏:c++专栏

你站在原地不动,就永远都是观众。

【学习目标】

  • 拷贝复制——赋值运算符重载

目录

🎓运算符重载的初步认识

🌈运算符重载

🌈赋值运算符重载格式 (上)

🌈operator__判断俩个日期是否相等

🎓运算符重载的深入认识

🌈赋值运算符重载格式(下)

👉拷贝构造和赋值运算符重载的区别 

👉格式(下) 

🌈默认赋值运算符重载

🌈❌重载成全局函数


🎓运算符重载的初步认识

🌈运算符重载

C++为了增强代码的可读性引入了运算符重载运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。

  • 函数名字为:关键字operator后面接需要重载的运算符符号
  • 函数原型:返回值类型 operator操作符(参数列表)
//判断真假
bool operator==(参数列表);
//返回类型Date 运算符=
Date operator=(参数列表);
注意:
  • 不能通过连接其他符号来创建新的操作符:比如operator@

  • 重载操作符必须有一个类类型参数

  • .*  ::  sizeof  ?:  . 注意以上5个运算符不能重载。这个经常在笔试选择题中出现。

🌈赋值运算符重载格式 (上)

  • 参数类型:const T&,传递引用可以提高传参效率

还有几个点我们后面会遇到问题提出的

  • 返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值
  • 检测是否自己给自己赋值
  • 返回*this :要复合连续赋值的含义

对于operator关键字来对俩个数据之间的操作,我们首先来敲一段


🌈operator__判断俩个日期是否相等

利用operator来实现《判断俩个日期是否相等,如果相等返回1,如果不相等返回0》

bool operator==(const Date& d1, const Date& d2)
{return d1._year == d2._year&& d1._month == d2._month&& d1._day ==d2._day;
}

这里会发现运算符重载成全局的就需要成员变量是公有的,那么问题来了,封装性如何保证?
这里其实可以用我们后面学习的友元解决,或者干脆重载成成员函数。(友元后期会告诉)
这里既然private里的成员变量无法访问。

第一种方法:给private改成public,运行成功。 

class Date
{
public:Date(int year, int month, int day){_year = year;_month = month;_day = day;}void print(){cout << _year << "-" << _month << "-" << _day << endl;}
//private:int _year;int _month;int _day;
};bool operator==(const Date& d1, const Date& d2)
{return d1._year == d2._year&& d1._month == d2._month&& d1._day ==d2._day;
}int main()
{Date d1(2023, 10, 5);Date d2(2023, 11, 5);cout << (d1 == d2) << endl;d1.print();d2.print();return 0;
}

 第二种方法:将重载成成员函数,在类中。

我们放进类中充当成员函数,就一定能实现嘛?看看能不能运行成功。

参数太多,可是我们就俩个对象,为什么显示参数太多呢?
——这里就提到了我们之前说的一个重要指针——this(C++编译器给每个“非静态的成员函数“增加了一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有“成员变量”的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。)

所以我们上面实际上是三个参数,只是this对用户来说透明的,不能显示传递。

//d1==d2//d1就相当于this,d2相当于形参列表里面一个,所以括号里面就只能有一个参数。
// bool operator==(Date* this, const Date& d2)// 这里需要注意的是,左操作数是this,指向调用函数的对象bool operator==(const Date& x){return _year == x._year&& _month == x._month&& _day == x._day;}

这里的判断俩个日期是否相等实际上就是再比较是否d1==d2?

d1就相当于this,d2相当于形参列表里面一个,所以括号里面就只能有一个参数。

这样就运行成功了。

判断俩个日期是否相等代码如下:

class Date
{
public:Date(int year, int month, int day){_year = year;_month = month;_day = day;}void print(){cout << _year << "-" << _month << "-" << _day << endl;}//d1==d2//d1就相当于this,d2相当于形参列表里面一个,所以括号里面就只能有一个参数。
// bool operator==(Date* this, const Date& d2)// 这里需要注意的是,左操作数是this,指向调用函数的对象bool operator==(const Date& x){return _year == x._year&& _month == x._month&& _day == x._day;}
private:int _year;int _month;int _day;
};int main()
{Date d1(2023, 10, 5);Date d2(2023, 11, 5);cout << (d1 == d2) << endl;/*d1.print();d2.print();*/return 0;
}

所以上面的代码实现了,运行没有问题


🎓运算符重载的深入认识

接下来我们了解了operator关键字的使用,我们接下来真正进入

赋值运算符重载

赋值运算符重载的内容——一个对象赋值给另一个对象。


🌈赋值运算符重载格式(下)

class Date
{
public:Date(int year, int month, int day){_year = year;_month = month;_day = day;}void print(){cout << _year << "-" << _month << "-" << _day << endl;}//拷贝构造Date(const Date& d){_year = d._year;_month = d._month;_day = d._day;}//赋值运算符重载void operator=(const Date& d){_year = d._year;_month = d._month;_day = d._day;}private:int _year;int _month;int _day;
};int main()
{Date d1(2023, 10, 5);Date d2(2023, 11, 5);d1 = d2;Date d3(d1);d1.print();d3.print();return 0;
}


👉拷贝构造和赋值运算符重载的区别 

我们针对上面一段代码来进行解读。看看这段代码有没有一些毛病或者一些优化的地方。

	Date d1(2023, 10, 5);Date d2(2023, 11, 5);d1 = d2;Date d3(d1);
这俩者有啥子区别呢?或者说
哪个是拷贝构造,哪个是赋值运算符重载呢?

  • 拷贝构造:是对同类对象初始化创建对象(创建一个新对象,然后给新对象初始化)

——就如上面代码的Date d3(d1)就是拷贝构造。

  • 赋值重载运算符:一个对象赋值给另一个对象(前提俩个对象都存在)

——就如上面代码的d1=d2就是赋值重载运算符。

光标对准d1=d2赋值重载运算符但是这里还没实现 ,按下fn+f10就可以看到

d2的成员变量的值赋值给了d1,继续走,创建的新的d3对象就引入了拷贝构造函数

三者都相等了,这就是运行的过程,大家可以自己敲一下来调试,进行查看。

ps:这些都是浅拷贝(值拷贝)但是日期类都是运行浅拷贝。

让我们继续来挑这段代码的毛病吧~

int i, j, k;i = j = k = 0;

对于这种赋值运算符,c语言允许不允许这样写?——允许(连续赋值)

0先赋值给k,这个表达式有个返回值,这个返回值是k,左边的操作数就是返回值,然后继续k赋值给j,j就是返回值,以此类推.......最后i的返回值就是0。

那么日期类支持嘛?
 

	Date d4, d5;d5 = d4 = d1;

因为这里从右往左,d1赋值给d4,返回值是void,所以是无法往前走。

所以这里正确的方法是什么?

这里从右往左,d1赋值给d4,返回值应该是d4,然后d4赋值给d5

所以我们就得探究 如何让d4是返回值,而不是void?

d4=d1;

d就是d1,this就是d4的地址。this的类型是(const Date&this),所以我们的返回类型是Date.

我们需要返回d4,如何返回d4呢?

我们当初说了this不能在形参和实参的位置给,但是可以在函数内部显示给。

//赋值运算符重载//d4=d1Date operator=(const Date& d){_year = d._year;_month = d._month;_day = d._day;return *this;}

👉格式(下) 

  • 返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值
  • 返回*this :要复合连续赋值的含义

        a.(全局对象和静态对象)出了作用域都还在,用引用返回效率更高。

        b. 局部对象出了作用域都不在了,不用引用返回.

那我们的这里的*this出了作用域还在不在?

//赋值运算符重载//d4=d1Date operator=(const Date& d){_year = d._year;_month = d._month;_day = d._day;return *this;}

——当然在啦

——因为首先我们要知道this是形参在栈区,出了作用域就会被销毁,那么这里是this嘛?是*this,*this是d4,d4的生命周期不再函数中,至少销毁了d4还在,*this只是一个中介而已。所以可以用引用返回。*this的别名是d4.

	//赋值运算符重载//d4=d1Date& operator=(const Date& d){_year = d._year;_month = d._month;_day = d._day;return *this;}

最后返回的是*this的别名那就是d4.

传值返回和传引用返回的区别:

传值返回:传值返回的是对象的拷贝,每一个operator赋值都是一次拷贝。(传值返回大多数是临时变量)

传引用返回:传引用返回的是*this的别名。

这样我们就可以连续赋值了。

  • 检测是否自己给自己赋值

大家有没有想过d1=d1是怎样的呢?是编译报错还是正常运行呢?

这是成功运行的。

我们来调试看看咋样?

这样也是可以的,this和&d都是自己。

如果你不想拥有自己与自己赋值,那么就可以加一个断言

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

d1=d1

if(this!=&d) ___this是d1的地址,&d就是d的地址。如果俩者地址都相同就不用赋值了。

🌈默认赋值运算符重载

用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝
注意:内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值。
默认生成赋值重载跟拷贝构造行为一样;
1.内置类型成员——值拷贝/浅拷贝  (Date)
2.自定义类型成员会去调用他的赋值重载 (MyQueue)
Stack是深拷贝,编译器自动生成的是浅拷贝。
如果我们不写赋值重载函数,编译器会不会自动生成?
class Date
{
public:Date(int year = 2003, int month = 10, int day = 5){_year = year;_month = month;_day = day;}void print(){cout << _year << "-" << _month << "-" << _day << endl;}//拷贝构造Date(const Date& d){_year = d._year;_month = d._month;_day = d._day;}//赋值运算符重载//Date& operator=(const Date& d)//{//	if (this != &d)//	{//		_year = d._year;//		_month = d._month;//		_day = d._day;//	}//	return *this;//}private:int _year;int _month;int _day;
};int main()
{Date d1(2023, 10, 5);Date d2(2023, 11, 5);d1 = d2;d1.print();d2.print();return 0;
}

我们给赋值运算符重载函数屏蔽调,看编译器是否会进行自动生成?

连续赋值呢?

所以默认生成的赋值运算符重载是可以实现连续对象赋值。

既然编译器生成的默认赋值运算符重载函数已经可以完成字节序的值拷贝了,但是和拷贝构造一样,并不是所有都是值拷贝,Date和Myqueue不需要我们自己实现赋值重载,因为Date是浅拷贝(值拷贝),Myqueue是自定义类型,但是Stack是需要自己去实现的,因为它是深拷贝,而默认生成的是浅拷贝.

🌈❌重载成全局函数

赋值运算符只能重载成类的成员函数不能重载成全局函数
就像命名空间域展开和全局变量一样的。
赋值运算符如果不显式实现,编译器会生成一个默认的。此时用户再在类外自己实现一个全局的
赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值运算符重载只能是类的成员函数。
能不能声明和定义分离?——可以。类外面给定义,类里面给声明,还是成员函数。

你站在原地不动,就永远都是观众。

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

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

相关文章

判断sparse matrix是否是对称矩阵

参考&#xff1a; https://stackoverflow.com/questions/48798893/error-in-checking-symmetric-sparse-matrix import scipy.sparse as sp import numpy as np np.random.seed(1)a sp.random(5, 5, density0.5)a结果如下 sym_err a - a.T sym_check_res np.all(np.abs(s…

IP协议相关技术

文章目录 IP协议相关技术仅凭IP无法完成通信DNSARP IP协议相关技术 仅凭IP无法完成通信 人们在上网的时候其实很少直接输入某个具体的IP地址。 在访问Web站点和发送、接收电子邮件时&#xff0c;我们通常会直接输入Web网站的地址或电子邮件地址等那些由应用层提供的地址&…

Python克隆单个网页

网上所有代码都无法完全克隆单个网页&#xff0c;不是Css&#xff0c;Js下载不下来就是下载下来也不能正常显示&#xff0c;只能自己写了&#xff0c;记得点赞~ 效果如图&#xff1a; 源码与所需的依赖&#xff1a; pip install requests pip install requests beautifulsoup4…

【vector题解】连续子数组的最大和 | 数组中出现次数超过一次的数字

连续子数组的最大和 连续子数组的最大和_牛客题霸_牛客网 描述 输入一个长度为n的整型数组array&#xff0c;数组中的一个或连续多个整数组成一个子数组&#xff0c;子数组最小长度为1。求所有子数组的和的最大值。 要求:时间复杂度为 O(n)&#xff0c;空间复杂度为 O(n) 进…

系列二、Shiro的核心组件

一、核心组件 # 1、UsernamePasswordToken 封装了用户的登录信息&#xff0c;使用用户的登录信息来创建Token # 2、SecurityManager Shiro的核心组件&#xff0c;负责安全认证和授权 # 3、Subject Shiro的一个抽象概念&#xff0c;包含了用户信息 # 4、Realm 开发者自定义的模块…

本地生活新赛道-视频号团购怎么做?

目前有在做实体行业的商家一定要看完&#xff0c;只要你进入了这个本地生活新的赛道&#xff0c;那你的生意自然会源源不断&#xff0c;那这个赛道又是什么呢&#xff1f; 这就是十月份刚刚上线的视频号团购项目&#xff0c;开通团购之后&#xff0c;就可以通过发短视频&#…

k8s:kubectl 详解

目录 1 kubectl 2 基本信息查看 2.1 查看 master 节点状态 2.2 查看命名空间 2.3 查看default命名空间的所有资源 2.4 创建命名空间app 2.5 删除命名空间app 2.6 在命名空间kube-public 创建副本控制器&#xff08;deployment&#xff09;来启动Pod&#xff08;nginx-wl…

Kubernetes实战(四)-部署docker harbor私有仓库

1 Docker原生私有仓库Registry 1.1 原生私有仓库Registry概述 Docker的仓库主要分两类&#xff1a; 私有仓库公有仓库 共有仓库只要在官方注册用户&#xff0c;登录即可使用。但对于仓库的使用&#xff0c;企业还是会有自己的专属镜像&#xff0c;所以私有库的搭建也是很有…

HarmonyOS ArkTS基础知识

概述 上一节&#xff0c;学习了TypeScript的基础语法&#xff0c;而在鸿蒙开发当中&#xff0c;有基于自己的编程语言&#xff0c;便是ArkTS。它是一种声明式UI的编程范式的语言&#xff0c;开发框架如下图所示&#xff1a; 根据框架图&#xff0c;分析&#xff0c;我将它大致…

JS点击图片指定对象变色两种方法

要求&#xff1a;点击上面的颜色实现下面的图像变成相同的颜色 难点&#xff1a;对于js函数的this对象不太清楚如何传递 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>changeColor</title>&l…

SpringCloud 微服务全栈体系(十三)

第十一章 分布式搜索引擎 elasticsearch 二、索引库操作 索引库就类似数据库表&#xff0c;mapping 映射就类似表的结构。 我们要向 es 中存储数据&#xff0c;必须先创建“库”和“表”。 1. mapping 映射属性 mapping 是对索引库中文档的约束&#xff0c;常见的 mapping …

nodejs+vue+python+PHP+微信小程序南七街道志愿者服务平台的设计与实现-计算机毕业设计推荐

目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1 1.3 研究的内容 1 第2章 相关技术 3 2.1 nodejs简介 4 2.2 express框架介绍 6 2.4 MySQL数据库 4 第3章 系统分析 5 3.1 需求分析 5 3.2 系统可行性分析 5 3.2.1技术可行性&#xff1a;…

代挂单页网址发布页+加盟代理+APP下载页源码

代挂单页加盟代理网址发布页app下载页HTML单页版本&#xff0c;自行修改源码内文字。自行修改联系方式、登录地址&#xff01;上传即可使用。源码我已全部打包好&#xff0c;直接上传本站提供的源码&#xff0c;无后台&#xff0c;直接访问即可&#xff01; 源码下载&#xff…

springboot 项目升级 2.7.16 踩坑

记录一下项目更新版本依赖踩坑 这个是项目最早的版本依赖 这里最初是最初是升级到 2.5.7 偷了个懒 这个版本的兼容性比较强 就选了这版本 也不用去修改就手动的去换了一下RabbitMQ的依赖 因为这边项目有AMQP 风险预警 1.spring-amqp版本低于2.4.17的用户应升级到2.4.17 2.spri…

Go 接口:nil接口为什么不等于nil?

本文主要内容:深入了解接口类型的运行时表示层。 文章目录 一、Go 接口的地位二、接口的静态特性与动态特性2.1 接口的静态特性与动态特性介绍2.2 “动静皆备”的特性的好处 三、nil error 值 ! nil四、接口类型变量的内部表示第一种&#xff1a;nil 接口变量第二种&#xff1a…

自动驾驶算法(九):多项式轨迹与Minimun Snap原理与Matab代码详解

目录 1 为什么需要轨迹优化 2 代码解析 3 完整代码 1 为什么需要轨迹优化 我们利用前八篇所学的博客可以利用RRT、A*、遗传算法等设计出一条折线轨迹&#xff0c;轨迹优化就是在路径优化的基础上将折线优化成曲线&#xff0c;这样更加有利于无人机的飞行。 那么什么是多项式轨…

Mybatis(一)

1. Mybatis简介 MyBatis下载地址 1.1 MyBatis历史 MyBatis最初是Apache的一个开源项目iBatis, 2010年6月这个项目由Apache Software Foundation迁移到了Google Code。随着开发团队转投Google Code旗下&#xff0c;iBatis3.x正式更名为MyBatis。代码于2013年11月迁移到Github…

Go语言的Json序列化与反序列化、Goto语法、Tcp Socket通信

目录标题 一、Json序列化与反序列化1. 序列化2. 反序列化 二、Goto语法三、Tcp Socket1. 单客户端发送信息到服务端2. 服务端客户端通信 一、Json序列化与反序列化 1. 序列化 package mainimport ("encoding/json""fmt")type Person struct {Name string…

【c趣编程】输入一个整数,判断其有几位

目录 1题目要求&#xff1a; 2解题思路&#xff1a; 3代码如下所示&#xff1a; 4运行代码如下&#xff1a; 5总结&#xff1a; 1题目要求&#xff1a; 只用一个scanf输出一串数&#xff0c;不可以一个一个的输入并计数&#xff0c;那样太浪费时间了。 C语言是一门面向过…

GoLong的学习之路(二十二)进阶,语法之并发(go最重要的特点)(channel的主要用法)

这一章是接上一章内容继续&#xff0c;上一章说到协程也就是goroutine&#xff0c;如何使用它&#xff0c;这一张是讲一种数据结构。当然这个章节的数据结构非常重要。可以说这个数据结构就是为了方便协程&#xff0c;才制作出来的。 单纯地将函数并发执行是没有意义的。函数与…