当前位置: 首页 > article >正文

【哇! C++】类和对象(五) - 赋值运算符重载

目录

​编辑

一、运算符重载

1.1 运算符重载概念

1.2 全局运算符重载

1.3 运算符重载为成员函数

二、赋值运算符重载的特性

2.1 赋值运算符重载需要注意的点

2.2 赋值运算符重载格式

2.2.1 传值返回

2.2.2 传引用返回

2.2.3 检查自己给自己赋值

三、赋值运算符重载的应用

四、总结


一、运算符重载

1.1 运算符重载概念

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

这里虽然用了重载,但是运算符重载和函数重载不是一个东西:

        函数重载:允许函数名相同参数不同的函数存在;

        运算符重载:让自定义类型的对象可以用运算操作符(必须是C\C++语法存在的运算符)。

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

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

1.2 全局运算符重载

使用全局的operator==,程序如下:

#include<iostream>
using namespace std;class Date
{
public:Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}//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(2025, 3, 8);Date d2(2025, 3, 7);cout << (d1 == d2) << endl;//cout << (operator==(d1, d2)) << endl;//两种写法是一样的return 0;
}

        上述程序中,全局的运算符重载的形式为:bool operator==(const Date& d1, const Date& d2),这就需要把Date类的成员变量改为私有,即注释掉private。

        注:赋值运算符重载成全局函数,注意重载成全局函数时没有this指针了,需要给两个参数。

        那么这样操作,就破坏了Date类的封装性。封装性如何保证?

  1. 使用友元函数;
  2. 重载为成员函数(常用)。

1.3 运算符重载为成员函数

        将上述程序作进一步修改:

#include<iostream>
using namespace std;class Date
{
public:Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}bool operator==(const Date& d){return this->_year == d._year&& this->_month == d._month&& this->_day == d._day;}private:int _year;int _month;int _day;    
};int main()
{Date d1(2025, 3, 8);Date d2(2025, 3, 7);cout << (d1 == d2) << endl;cout << d1.operator==(d2) << endl;//两种写法是一样的return 0;
}

        运算符重载为成员函数的形式为:bool operator==(const Date& d),这里需要注意的是,左操作数是this,指向调用函数的对象。

1.4 运算符重载需要注意的点

运算符重载的使用需要注意一下5点:

        1. 不能通过连接其他符号来创建新的操作符:比如operator@;

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

        3. 用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义;

        4. 作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐
藏的this;

        5. .*   : :   sizeof   ?:   . 注意以上5个运算符不能重载。

        针对5中的.*,可以写如下程序:

class ob
{
public:void func(){cout << "void func()" << endl;}
};typedef void(ob::*pobfunc)()int main()
{pobfunc p = &ob::func;//成员函数取地址,要用&操作符,不然取不到//等同于void (ob:: *pi)() = &ob::func;ob tmp;(tmp.*p)();//通过对象去调用成员函数的指针,成员函数指针要传this//*p();//普通的函数指针的调用return 0;
}

        函数指针和数组指针都是特殊的指针,普通变量的重命名为:typedef 类型 重命名

        上述程序中,typedef void(ob::*pobfunc)()成员函数指针类型的重定义。其中,pobfunc是指向ob类中成员函数的函数指针类型。

1. void (ob*::)()

        函数指针类型,它指向一个返回值为void,且没有参数的成员函数。ob*::表示函数指针指向ob类的成员函数。

2. typedef void(ob*::pobfunc)()

        使用typedef关键字重定义一个指向ob类中的成员函数的函数指针类型pobfunc。

二、赋值运算符重载

2.1 赋值运算符重载格式

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

        相较于传值传参和传引用传参。 

2.1.1 传值返回

        将1.3的程序在Date类中进行补充,补充的程序为:

Date operator=(const Date& d2)
{this->_year = d2._year;this->_month = d2._month;this->_day = d2._day;return *this;//*this就是d1,相当于拿到左操作数
}

        因为,Date operator=(const Date& d)中的Date表明是传值返回,意味着return *this;不会返回*this而是返回它的拷贝(拷贝以后或存放在寄存器中)。

        所以,同类型的传值拷贝又会调用一个拷贝构造。

2.1.2 传引用返回

        将1.3的程序在Date类中进行补充,补充的程序为:

Date& operator=(const Date& d2)
{this->_year = d2._year;this->_month = d2._month;this->_day = d2._day;return *this;//*this就是d1,相当于拿到左操作数
}

2.1.3 检查自己给自己赋值

        这可能会造成性能的浪费;成员变量可能依赖于其他成员变量的值,如果这些成员变量的值被覆盖,可能会引发错误。

        基于2.2.2,可通过判断地址来进一步改写:

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

2.2 赋值运算符只能重载成类的成员函数

        C++规定,其他运算符可以重载成全局的,赋值重载不可以,只能重载为成员函数。 

        对于默认成员函数,如果不写,编译器会生成一份。如果放在全局,类中没有,编译器会生成一份,那调用的时候会产生冲突。 

class Date
{
public:Date(int year = 1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;}int _year;int _month;int _day;
};
// 赋值运算符重载成全局函数,注意重载成全局函数时没有this指针了,需要给两个参数Date& operator=(Date& left, const Date& right)
{if (&left != &right){left._year = right._year;left._month = right._month;left._day = right._day;}return left;
}// error C2801: “operator =”必须是非静态成员

2.3 没有显式,编译器会生成一个默认赋值运算符重载

        不写赋值运算符重载,编译器会不会生成默认的呢? - 会,因为是6个默认成员函数之一。

        默认生成的对内置类型会完成值拷贝(浅拷贝),对自定义类型会去再调用它的赋值。

        怎么知道赋值默认生成的赋值的行为是什么? - 同拷贝构造。

        那是不是意味着自定义赋值操作符重载就可以不写了呢? - 不是。

        注意:如果类中未涉及到资源管理,赋值运算符是否实现都可以;一旦涉及到资源管理则必
须要实现。 

三、赋值运算符重载的应用

        如果是内置类型,编译器是可以调用相关指令的;如果是自定义类型,编译器首先会去看有没有重载运算符,如果没有就会报错。

#include<iostream>
using namespace std;class Date
{
public:Date(int _year = 1, int _month = 1, int _day = 1){_year = year;_month = month;_day = day;}Date& operator=(const Date& d){if(this != &d){this->_year = d._year;this->_month = d._month;this->_day = d._day;}return *this;}Print(){cout << _year << "-" << _month << "-" << _day << endl;}private:int _year;int _month;int _day;
};int main()
{Date d1(2025, 3, 9);Date d2(2025, 3, 9);d1 = d2;Date d3(d1);d1 = d2 = d3;//自定义类型,连续赋值是要有返回值的d3.Print();int i, j = 0;cout << (i = j = 10) << endl;return 0;
}

        程序Date d3(d1);为拷贝构造,还是一个构造。构造是指对象创建实例化的时候自动调用的初始化。其他的构造可能是用一些普通的参数进行初始化,而拷贝构造是用同类型一个存在的对象进行初始化要创建的对象。

        程序d1 = d2;,已经存在的两个对象,一个拷贝赋值给另一个,这里边用到了=运算符,所以就要重载这个运算符。

        程序d1 = d2 = d3;为自定义类型,连续赋值是要有返回值的。

        程序i = j = 10;,内置类型支持连续赋值。执行动作为:10赋值给j作为一个表达式,这个表达式有返回值,返回值就是左操作数j。同理,再向左,返回值为左操作数i。

四、总结

默认生成的函数行为总结:

  • 构造和析构:内置类型不处理,自定义类型调用对应的构造和析构。
  • 拷贝构造和赋值运算符重载:内置类型值拷贝,自定义类型调用对应的拷贝构造和赋值重载。

相关文章:

【哇! C++】类和对象(五) - 赋值运算符重载

目录 ​编辑 一、运算符重载 1.1 运算符重载概念 1.2 全局运算符重载 1.3 运算符重载为成员函数 二、赋值运算符重载的特性 2.1 赋值运算符重载需要注意的点 2.2 赋值运算符重载格式 2.2.1 传值返回 2.2.2 传引用返回 2.2.3 检查自己给自己赋值 三、赋值运算符重载的…...

基于单片机的风速报警装置设计

标题:基于单片机的风速报警装置设计 内容:1.摘要 本设计聚焦于基于单片机的风速报警装置&#xff0c;旨在解决传统风速监测缺乏实时报警功能的问题。采用单片机作为核心控制单元&#xff0c;结合风速传感器采集风速数据。经实验测试&#xff0c;该装置能准确测量 0 - 60m/s 范…...

Linux一键安装zsh终端美化插件

zsh应该是很多人第一个用的Linux终端美化软件 但是其安装略微复杂&#xff0c;让人有些困扰 所以我花了两天写了一键安装脚本&#xff0c;实测运行后直接安装好 适用于Ubuntu、Debian、Red Hat、macOS等系统 直接安装好zsh 以及常用插件 autojump 跳转插件 zsh-syntax-highlig…...

Docker部署Ragflow(完美解决502 bad gateway)

Docker快速启动Ragflow:Dev 系统准备 ubuntu server 24.04 CPU ≥ 4 cores (x86);RAM ≥ 16 GB;Disk ≥ 100 GB; 更新系统 sudo apt update 下载源码 git clone https://github.com/infiniflow/ragflow.git cd ragflow/docker # 切换稳定版本分支 git checkout -f v0.17.…...

C++ 算法竞赛STL以及常见模板

目录 STL /*═══════════════ Vector ═══════════════*/ /*════════════════ Pair ════════════════*/ /*══════════════ String ════════════════*/ /*══════════…...

前端数据模拟 Mock.js 学习笔记(附带详细)

前端数据模拟 Mock.js 学习笔记 在前端开发过程中&#xff0c;数据模拟是一项至关重要的环节。当后端接口尚未完成或者需要独立进行前端开发与测试时&#xff0c;Mock.js 能发挥巨大作用&#xff0c;它可以模拟各种数据场景&#xff0c;助力前端开发高效进行。 一、Mock.js 的…...

Web基础:HTML快速入门

HTML基础语法 HTML&#xff08;超文本标记语言&#xff09; 是用于创建网页内容的 标记语言&#xff0c;通过定义页面的 结构和内容 来告诉浏览器如何呈现网页。 超文本&#xff08;Hypertext&#xff09; 是一种通过 链接&#xff08;Hyperlinks&#xff09; 将不同文本、图像…...

学习一下Qt中的分裂器以及为什么要使用分裂器?

关于分裂器的使用和内容这篇文章写的很详细了[QT_043]Qt学习之分裂器&#xff08;QSplitter&#xff09;-CSDN博客 我就说一下为什么已经有布局器要还使用分裂器 在 Qt 中&#xff0c;布局时使用分裂器&#xff08;QSplitter&#xff09;主要有以下几个方面的好处&#xff…...

Xshell链接Linux机器更换jdk版本

都25年了&#xff0c;不会还有人在用 jdk1.8 吧? 那么问题来了&#xff0c;如果公司让你更新机器上的jdk版本&#xff0c;你又刚好是一个小白没操作过&#xff0c;怎么办&#xff1f;别急&#xff0c;步骤这就奉上~ 编辑 .bashrc 或 .zshrc 配置文件&#xff1a; 如果你使用的…...

2025.3.3总结

周一这天&#xff0c;我约了绩效教练&#xff0c;主要想了解专业类绩效的考核方式以及想知道如何拿到一个更好的绩效。其他的岗位并不是很清楚&#xff0c;但是专业类的岗位&#xff0c;目前采取绝对考核&#xff0c;管理层和专家岗采取相对考核&#xff0c;有末尾淘汰。 通过…...

如何应用大模型 — 大模型使用范式

从OpenAI发布ChatGPT开始&#xff0c;大模型就开始受到大家关注&#xff0c;到DeepSeek-R1出现&#xff0c;大家的关注达到了顶峰&#xff0c;越来越多的企业&#xff0c;机构&#xff0c;学校&#xff0c;政府部门希望接入大模型&#xff0c;希望通过大模型来提升效率&#xf…...

DeepSeek本机部署(基于Ollama和Docker管理)

目录 一、ollama 与 docker 简介 &#xff08;一&#xff09;ollama(Ollama) &#xff08;二&#xff09;docker 二、利用 ollama 和 docker 配置 deepseek-r1 的准备工作 &#xff08;一&#xff09;硬件需求 &#xff08;二&#xff09;软件安装 三、配置 deepseek-r1…...

C++复试笔记(一)

Setw 是C中用于设置输出字段宽度的函数。当使用 setw(3) 时&#xff0c;它会设置紧接着的输出字段的最小宽度为3个字符。如果字段内容长度小于3&#xff0c;则会在左侧填充空格以达到指定宽度&#xff1b;如果内容长度大于或等于3&#xff0c;则全部内容将被输出&#xff0c;…...

如何让一个类作为可调用对象被thread调用?

如何让一个类作为可调用对象&#xff0c;被 std::thread 调用 在 C 中&#xff0c;可以让一个类对象作为可调用对象&#xff08;Callable Object&#xff09;&#xff0c;然后用 std::thread 进行调用。要实现这一点&#xff0c;主要有三种方法&#xff1a; 重载 operator()&…...

学习小程序开发--Day1

项目学习开篇 项目架构 项目进程 创建uni-app项目 通过HBuilderX创建 小结 page.json 和 tabBar 目录文件 pages.json的配置...

“量子心灵AI“的监控仪表盘 - javascript网页设计案例

【前端实战】基于Three.js和Chart.js打造未来科技风AI监控仪表盘 本文通过AI辅助开发&#xff0c;详细记录了一个高级前端项目的完整实现过程。文章包含核心代码片段、技术要点及遇到的问题与解决方案。适合有一定前端基础的开发者学习参考。 1. 项目概述 本文详细介绍了一个名…...

Redis 中 string 和 list 的原理说明

Redis 中 string 和 list 的底层实现 Redis有5种基础数据结构&#xff0c;对应的value分别为&#xff1a;string (字符串)、list (列表)、set (集合)、hash (哈希) 和 zset (有序集合) Redis 对象头结构体&#xff1a; struct RedisObject {int4 type; // 4bits 对象的基本类型…...

JVM常用概念之String.intern()

问题 String.intern()的工作原理&#xff1f;我们应该如何使用它? 基础知识 字符串池&#xff08;String Pool&#xff09; String类在我们日常编程工作中是使用频率非常高的一种对象类型。JVM为了提升性能和减少内存开销&#xff0c;避免字符串的重复创建&#xff0c;其维…...

DeepLabv3+改进6:在主干网络中添加SegNext_Attention|助力涨点

🔥【DeepLabv3+改进专栏!探索语义分割新高度】 🌟 你是否在为图像分割的精度与效率发愁? 📢 本专栏重磅推出: ✅ 独家改进策略:融合注意力机制、轻量化设计与多尺度优化 ✅ 即插即用模块:ASPP+升级、解码器 PS:订阅专栏提供完整代码 目录 论文简介 步骤一 步骤二…...

亚信安全发布2024威胁年报和2025威胁预测

在当今数字化时代&#xff0c;网络空间已成为全球经济、社会和国家安全的核心基础设施。随着信息技术的飞速发展&#xff0c;网络连接了全球数十亿用户&#xff0c;推动了数字经济的蓬勃发展&#xff0c;同时也带来了前所未有的安全挑战。2024年&#xff0c;网络安全形势愈发复…...

深入理解 DOM 元素

深入理解 DOM 元素&#xff1a;构建动态网页的基石 在网页开发的世界里&#xff0c;DOM&#xff08;Document Object Model&#xff0c;文档对象模型&#xff09;元素宛如一座桥梁&#xff0c;连接着静态的 HTML 结构与动态的 JavaScript 交互逻辑。它让原本呆板的网页变得鲜活…...

[数据分享第七弹]全球洪水相关数据集

洪水是一种常见的自然灾害&#xff0c;在全球范围内造成了极为严重的威胁。近年来&#xff0c;针对洪水事件的检测分析&#xff0c;以及对于洪水灾害和灾后恢复能力的研究日渐增多&#xff0c;也产生了众多洪水数据集。今天&#xff0c;我们一起来收集整理一下相关数据集。&…...

SpringBoot POST和GET请求

1. 什么是 HTTP 请求&#xff1f; HTTP 协议&#xff1a;超文本传输协议&#xff0c;用于客户端和服务器之间的通信。 常见 HTTP 方法&#xff1a; GET&#xff1a;获取资源POST&#xff1a;提交数据PUT&#xff1a;更新资源DELETE&#xff1a;删除资源 2. GET 请求详解 作…...

MySQL 面试篇

MySQL相关面试题 定位慢查询 **面试官&#xff1a;**MySQL中&#xff0c;如何定位慢查询? 我们当时做压测的时候有的接口非常的慢&#xff0c;接口的响应时间超过了2秒以上&#xff0c;因为我们当时的系统部署了运维的监控系统Skywalking &#xff0c;在展示的报表中可以看到…...

【Andrej Karpathy 神经网络从Zero到Hero】--2.语言模型的两种实现方式 (Bigram 和 神经网络)

目录 统计 Bigram 语言模型质量评价方法 神经网络语言模型 【系列笔记】 【Andrej Karpathy 神经网络从Zero到Hero】–1. 自动微分autograd实践要点 本文主要参考 大神Andrej Karpathy 大模型讲座 | 构建makemore 系列之一&#xff1a;讲解语言建模的明确入门&#xff0c;演示…...

Android MVC、MVP、MVVM三种架构的介绍和使用。

写在前面&#xff1a;现在随便出去面试Android APP相关的工作&#xff0c;面试官基本上都会提问APP架构相关的问题&#xff0c;用Java、kotlin写APP的话&#xff0c;其实就三种架构MVC、MVP、MVVM&#xff0c;MVC和MVP高度相似&#xff0c;区别不大&#xff0c;MVVM则不同&…...

python使用django搭建图书管理系统

大家好,你们喜欢的梦幻编织者回来了 随着计算机网络和信息技术的不断发展&#xff0c;人类信息交流的方式从根本上发生了改变&#xff0c;计算机技术、信息化技术在各个领域都得到了广泛的应用。图书馆的规模和数量都在迅速增长&#xff0c;馆内藏书也越来越多&#xff0c;管理…...

JavaScript系列06-深入理解 JavaScript 事件系统:从原生事件到 React 合成事件

JavaScript 事件系统是构建交互式 Web 应用的核心。本文从原生 DOM 事件到 React 的合成事件&#xff0c;内容涵盖&#xff1a; JavaScript 事件基础&#xff1a;事件类型、事件注册、事件对象事件传播机制&#xff1a;捕获、目标和冒泡阶段高级事件技术&#xff1a;事件委托、…...

大话机器学习三大门派:监督、无监督与强化学习

以武侠江湖为隐喻&#xff0c;系统阐述了机器学习的三大范式&#xff1a;​监督学习&#xff08;少林派&#xff09;​凭借标注数据精准建模&#xff0c;擅长图像分类等预测任务&#xff1b;无监督学习&#xff08;逍遥派&#xff09;​通过数据自组织发现隐藏规律&#xff0c;…...

win11编译llama_cpp_python cuda128 RTX30/40/50版本

Geforce 50xx系显卡最低支持cuda128&#xff0c;llama_cpp_python官方源只有cpu版本&#xff0c;没有cuda版本&#xff0c;所以自己基于0.3.5版本源码编译一个RTX 30xx/40xx/50xx版本。 1. 前置条件 1. 访问https://developer.download.nvidia.cn/compute/cuda/12.8.0/local_…...