【哇! 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.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 检查自己给自己赋值 三、赋值运算符重载的…...
SpringCloud系列教程(十三):Sentinel流量控制
SpringCloud中的注册、发现、网关、服务调用都已经完成了,现在就剩下最后一部分,就是关于网络控制。SpringCloud Alibaba这一套中间件做的非常好,把平时常用的功能都集成进来了,而且非常简单高效。我们下一步就完成最后一块拼图Se…...
vue+element|el-tree树设置懒加载和设置默认勾选
文章目录 导文代码实现1. 基本结构2. 懒加载实现3. 默认勾选功能4. 动态加载初始节点5. 节点勾选事件监听完整代码 导文 在实际开发中,很多数据过于庞大,需要分批请求,使用到懒加载。但是在tree的方法中,使用懒加载后无法直接使用…...
零售交易流程相关知识(top-down拆解)
引入 关于POS机交易时的后台数据交互 模块之间数据交换,都可以能被窃取或篡改。由此引入加密、解密机制和签名、验签机制 经典的加密、解密机制: 对称加密:DES\ TDES\ AES\ RC4 非对称加密:RSA\ DSA\ ECC 经典的签名、验签…...
混合存储HDD+SSD机型磁盘阵列,配上SSD缓存功能,性能提升300%
企业日常运行各种文件无处不在,文档、报告、视频、应用数据......面对成千上万的文件,团队之间需要做到无障碍协作,员工能够即时快速访问、共享处理文件。随着业务增长,数字化办公不仅需要大容量,快速高效的文件访问越…...
将本地已有的项目上传至仓库
上传的仓库为Gitee 进入项目目录: 使用命令行工具进入你想要上传的项目的根目录。 初始化Git仓库: 如果项目目录尚未初始化为Git仓库,执行以下命令: git init 执行完成后,项目根目录下会自动生成一个隐藏的.git文件夹…...
中级网络工程师面试题参考示例(3)
一、企业园区网络 问题1:如何实现园区网络的自动化部署和管理?请结合实际场景说明技术选型。 答案要点: 技术选型: SDN(软件定义网络):通过控制器(如Cisco DNA Center)…...
祝福语【算法赛】
题目来源:第 27 场 蓝桥入门赛【算法题】 可以参考一下,本人也是比较菜 不喜勿喷,求求求 import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner sc new Scanner(System.in);String S sc.nextLi…...
前端 | CORS 跨域问题解决
问题:Access to fetch at http://localhost:3000/save from origin http://localhost:5174 has been blocked by CORS policy: Response to preflight request doesnt pass access control check: No Access-Control-Allow-Origin header is present on the request…...
MySQL知识点(第一部分)
MySQL 基础: 1、SQL语句的分类: DDL:用于控制数据库的操作DML:用于控制表结构的字段,增、删、修DQL:用于查询语句DCL:用于管理数据库,用户,数据库的访问 权限。 2、M…...
ChatGPT使用经验分享
ChatGPT 3.5模型 与 4模型的区别 ChatGPT 3.5 示例 问:树上有9只鸟,打死了一只还剩几只? 答:如果打死了一只鸟,那么树上还剩下8只鸟。 ChatGPT 4 示例 问:树上有9只鸟,打死了一只还剩几只&…...
Webshell原理与利用
本文内容仅用于技术研究、网络安全防御及合法授权的渗透测试,严禁用于任何非法入侵、破坏或未经授权的网络活动。 1. WebShell的定义与原理 定义:WebShell是一种基于Web脚本语言(如PHP、ASP、JSP)编写的恶意后门程序,…...
Java直通车系列15【Spring MVC】(ModelAndView 使用)
目录 1. ModelAndView 概述 2. ModelAndView 的主要属性和方法 主要属性 主要方法 3. 场景示例 示例 1:简单的 ModelAndView 使用 示例 2:使用 ModelAndView 处理列表数据 示例 3:使用 ModelAndView 处理异常情况 1. ModelAndView 概…...
大模型系列课程学习-基于Vllm/Ollama/Ktransformers完成Deepseek推理服务部署
1.机器配置及实验说明 基于前期搭建的双卡机器装机教程,配置如下: 硬件名称参数备注CPUE5-2680V42 *2(线程28个)无GPU2080TI-22G 双卡魔改卡系统WSL Unbuntu 22.04.5 LTS虚拟机 本轮实验目的:基于VLLM/Ollama/ktran…...
基于深度文档理解的开源 RAG 引擎RAGFlow的介绍和安装
目录 前言1. RAGFlow 简介1.1 什么是 RAGFlow?1.2 RAGFlow 的核心特点 2. RAGFlow 的安装与配置2.1 硬件与软件要求2.2 下载 RAGFlow 源码2.3 源码编译 Docker 镜像2.4 设置完整版(包含 embedding 模型)2.5 运行 RAGFlow 3. RAGFlow 的应用场…...
DNS Beaconing
“DNS Beaconing” 是一种隐蔽的网络通信技术,通常与恶意软件(如木马、僵尸网络)相关。攻击者通过定期发送 DNS请求 到受控的域名服务器(C&C服务器),实现与恶意软件的隐蔽通信、数据传输或指令下发。由…...
【论文阅读】多模态——LSeg
文献基本信息 标题:Language-Driven Semantic Segmentation作者:Boyi Li、Kilian Q. Weinberger、Serge Belongie、Vladlen Koltun、Ren Ranftl单位:Cornell University、University of Copenhagen、Apple、Intel Labs会议/期刊:…...
vue3如何配置环境和打包
很多新手友友们或刚从vue2切换到vue3的同学,对vue3不同环境配置和打包有很多困惑的地方,Jenna这就把vue3打包配置流程详细的写下来,你们只需要copy就好啦 1.创建环境文件 当我们把项目拿到手,只需要创建三个环境文件:…...
高并发下订单库存防止超卖策略
文章目录 什么是超卖问题?推荐策略:Redis原子操作(Redis incr)乐观锁lua脚本利用Redis increment 的原子操作,保证库存数安全update使用乐观锁LUA脚本保持库存原子性 什么是超卖问题? 在并发的场景下,比如商城售卖商品…...
vue安装stylelint
执行 npm install -D stylelint postcss-html stylelint-config-recommended-vue stylelint-config-standard stylelint-order stylelint-prettier postcss-less stylelint-config-property-sort-order-smacss 安装依赖,这里是less,sass换成postcss-scss…...
vscode里如何用git
打开vs终端执行如下: 1 初始化 Git 仓库(如果尚未初始化) git init 2 添加文件到 Git 仓库 git add . 3 使用 git commit 命令来提交你的更改。确保在提交时加上一个有用的消息。 git commit -m "备注信息" 4 …...
多模态2025:技术路线“神仙打架”,视频生成冲上云霄
文|魏琳华 编|王一粟 一场大会,聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中,汇集了学界、创业公司和大厂等三方的热门选手,关于多模态的集中讨论达到了前所未有的热度。其中,…...
IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)
文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...
企业如何增强终端安全?
在数字化转型加速的今天,企业的业务运行越来越依赖于终端设备。从员工的笔记本电脑、智能手机,到工厂里的物联网设备、智能传感器,这些终端构成了企业与外部世界连接的 “神经末梢”。然而,随着远程办公的常态化和设备接入的爆炸式…...
Go 语言并发编程基础:无缓冲与有缓冲通道
在上一章节中,我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道,它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好࿰…...
JavaScript 数据类型详解
JavaScript 数据类型详解 JavaScript 数据类型分为 原始类型(Primitive) 和 对象类型(Object) 两大类,共 8 种(ES11): 一、原始类型(7种) 1. undefined 定…...
免费数学几何作图web平台
光锐软件免费数学工具,maths,数学制图,数学作图,几何作图,几何,AR开发,AR教育,增强现实,软件公司,XR,MR,VR,虚拟仿真,虚拟现实,混合现实,教育科技产品,职业模拟培训,高保真VR场景,结构互动课件,元宇宙http://xaglare.c…...
【 java 虚拟机知识 第一篇 】
目录 1.内存模型 1.1.JVM内存模型的介绍 1.2.堆和栈的区别 1.3.栈的存储细节 1.4.堆的部分 1.5.程序计数器的作用 1.6.方法区的内容 1.7.字符串池 1.8.引用类型 1.9.内存泄漏与内存溢出 1.10.会出现内存溢出的结构 1.内存模型 1.1.JVM内存模型的介绍 内存模型主要分…...
解决:Android studio 编译后报错\app\src\main\cpp\CMakeLists.txt‘ to exist
现象: android studio报错: [CXX1409] D:\GitLab\xxxxx\app.cxx\Debug\3f3w4y1i\arm64-v8a\android_gradle_build.json : expected buildFiles file ‘D:\GitLab\xxxxx\app\src\main\cpp\CMakeLists.txt’ to exist 解决: 不要动CMakeLists.…...
Spring Security 认证流程——补充
一、认证流程概述 Spring Security 的认证流程基于 过滤器链(Filter Chain),核心组件包括 UsernamePasswordAuthenticationFilter、AuthenticationManager、UserDetailsService 等。整个流程可分为以下步骤: 用户提交登录请求拦…...

