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

【哇! 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 检查自己给自己赋值 三、赋值运算符重载的…...

SpringCloud系列教程(十三):Sentinel流量控制

SpringCloud中的注册、发现、网关、服务调用都已经完成了&#xff0c;现在就剩下最后一部分&#xff0c;就是关于网络控制。SpringCloud Alibaba这一套中间件做的非常好&#xff0c;把平时常用的功能都集成进来了&#xff0c;而且非常简单高效。我们下一步就完成最后一块拼图Se…...

vue+element|el-tree树设置懒加载和设置默认勾选

文章目录 导文代码实现1. 基本结构2. 懒加载实现3. 默认勾选功能4. 动态加载初始节点5. 节点勾选事件监听完整代码 导文 在实际开发中&#xff0c;很多数据过于庞大&#xff0c;需要分批请求&#xff0c;使用到懒加载。但是在tree的方法中&#xff0c;使用懒加载后无法直接使用…...

零售交易流程相关知识(top-down拆解)

引入 关于POS机交易时的后台数据交互 模块之间数据交换&#xff0c;都可以能被窃取或篡改。由此引入加密、解密机制和签名、验签机制 经典的加密、解密机制&#xff1a; 对称加密&#xff1a;DES\ TDES\ AES\ RC4 非对称加密&#xff1a;RSA\ DSA\ ECC 经典的签名、验签…...

混合存储HDD+SSD机型磁盘阵列,配上SSD缓存功能,性能提升300%

企业日常运行各种文件无处不在&#xff0c;文档、报告、视频、应用数据......面对成千上万的文件&#xff0c;团队之间需要做到无障碍协作&#xff0c;员工能够即时快速访问、共享处理文件。随着业务增长&#xff0c;数字化办公不仅需要大容量&#xff0c;快速高效的文件访问越…...

将本地已有的项目上传至仓库

上传的仓库为Gitee 进入项目目录&#xff1a; 使用命令行工具进入你想要上传的项目的根目录。 初始化Git仓库&#xff1a; 如果项目目录尚未初始化为Git仓库&#xff0c;执行以下命令&#xff1a; git init 执行完成后&#xff0c;项目根目录下会自动生成一个隐藏的.git文件夹…...

中级网络工程师面试题参考示例(3)

一、企业园区网络 问题1&#xff1a;如何实现园区网络的自动化部署和管理&#xff1f;请结合实际场景说明技术选型。 答案要点&#xff1a; 技术选型&#xff1a; SDN&#xff08;软件定义网络&#xff09;&#xff1a;通过控制器&#xff08;如Cisco DNA Center&#xff09;…...

祝福语【算法赛】

题目来源&#xff1a;第 27 场 蓝桥入门赛【算法题】 可以参考一下&#xff0c;本人也是比较菜 不喜勿喷&#xff0c;求求求 import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner sc new Scanner(System.in);String S sc.nextLi…...

前端 | CORS 跨域问题解决

问题&#xff1a;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 基础&#xff1a; 1、SQL语句的分类&#xff1a; DDL&#xff1a;用于控制数据库的操作DML&#xff1a;用于控制表结构的字段&#xff0c;增、删、修DQL&#xff1a;用于查询语句DCL&#xff1a;用于管理数据库&#xff0c;用户&#xff0c;数据库的访问 权限。 2、M…...

ChatGPT使用经验分享

ChatGPT 3.5模型 与 4模型的区别 ChatGPT 3.5 示例 问&#xff1a;树上有9只鸟&#xff0c;打死了一只还剩几只&#xff1f; 答&#xff1a;如果打死了一只鸟&#xff0c;那么树上还剩下8只鸟。 ChatGPT 4 示例 问&#xff1a;树上有9只鸟&#xff0c;打死了一只还剩几只&…...

Webshell原理与利用

本文内容仅用于技术研究、网络安全防御及合法授权的渗透测试&#xff0c;严禁用于任何非法入侵、破坏或未经授权的网络活动。 1. WebShell的定义与原理 定义&#xff1a;WebShell是一种基于Web脚本语言&#xff08;如PHP、ASP、JSP&#xff09;编写的恶意后门程序&#xff0c;…...

Java直通车系列15【Spring MVC】(ModelAndView 使用)

目录 1. ModelAndView 概述 2. ModelAndView 的主要属性和方法 主要属性 主要方法 3. 场景示例 示例 1&#xff1a;简单的 ModelAndView 使用 示例 2&#xff1a;使用 ModelAndView 处理列表数据 示例 3&#xff1a;使用 ModelAndView 处理异常情况 1. ModelAndView 概…...

大模型系列课程学习-基于Vllm/Ollama/Ktransformers完成Deepseek推理服务部署

1.机器配置及实验说明 基于前期搭建的双卡机器装机教程&#xff0c;配置如下&#xff1a; 硬件名称参数备注CPUE5-2680V42 *2&#xff08;线程28个&#xff09;无GPU2080TI-22G 双卡魔改卡系统WSL Unbuntu 22.04.5 LTS虚拟机 本轮实验目的&#xff1a;基于VLLM/Ollama/ktran…...

基于深度文档理解的开源 RAG 引擎RAGFlow的介绍和安装

目录 前言1. RAGFlow 简介1.1 什么是 RAGFlow&#xff1f;1.2 RAGFlow 的核心特点 2. RAGFlow 的安装与配置2.1 硬件与软件要求2.2 下载 RAGFlow 源码2.3 源码编译 Docker 镜像2.4 设置完整版&#xff08;包含 embedding 模型&#xff09;2.5 运行 RAGFlow 3. RAGFlow 的应用场…...

DNS Beaconing

“DNS Beaconing” 是一种隐蔽的网络通信技术&#xff0c;通常与恶意软件&#xff08;如木马、僵尸网络&#xff09;相关。攻击者通过定期发送 DNS请求 到受控的域名服务器&#xff08;C&C服务器&#xff09;&#xff0c;实现与恶意软件的隐蔽通信、数据传输或指令下发。由…...

【论文阅读】多模态——LSeg

文献基本信息 标题&#xff1a;Language-Driven Semantic Segmentation作者&#xff1a;Boyi Li、Kilian Q. Weinberger、Serge Belongie、Vladlen Koltun、Ren Ranftl单位&#xff1a;Cornell University、University of Copenhagen、Apple、Intel Labs会议/期刊&#xff1a;…...

vue3如何配置环境和打包

很多新手友友们或刚从vue2切换到vue3的同学&#xff0c;对vue3不同环境配置和打包有很多困惑的地方&#xff0c;Jenna这就把vue3打包配置流程详细的写下来&#xff0c;你们只需要copy就好啦 1.创建环境文件 当我们把项目拿到手&#xff0c;只需要创建三个环境文件&#xff1a…...

高并发下订单库存防止超卖策略

文章目录 什么是超卖问题&#xff1f;推荐策略&#xff1a;Redis原子操作(Redis incr)乐观锁lua脚本利用Redis increment 的原子操作&#xff0c;保证库存数安全update使用乐观锁LUA脚本保持库存原子性 什么是超卖问题&#xff1f; 在并发的场景下&#xff0c;比如商城售卖商品…...

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 安装依赖&#xff0c;这里是less&#xff0c;sass换成postcss-scss…...

用Deepseek写一个 HTML 和 JavaScript 实现一个简单的飞机游戏

大家好&#xff01;今天我将分享如何使用 HTML 和 JavaScript 编写一个简单的飞机游戏。这个游戏的核心功能包括&#xff1a;控制飞机移动、发射子弹、敌机生成、碰撞检测和得分统计。代码简洁易懂&#xff0c;适合初学者学习和实践。 游戏功能概述 玩家控制&#xff1a;使用键…...

three.js 在 webGL 添加纹理

在我们生成了3D设计之后&#xff0c;我们可以添加纹理使其更加吸引人。在 webGL 和 p5.js中&#xff0c;可以使用 gl.texImage2D() 和 texture() API来为形状应用纹理。 使用 webGL 在 webGL 中&#xff0c;gl.texImage2D() 函数用于从图像文件生成2D纹理。该函数接受许多参…...

【5】单调队列学习笔记

前言 鸽了很久&#xff0c; 2023 / 1 / 5 2023/1/5 2023/1/5 开始&#xff0c; 2023 / 1 / 21 2023/1/21 2023/1/21 才完工。 中途去集训了&#xff0c;没时间来补漏洞。 单调队列 单调队列是一种非常实用的数据结构&#xff0c;可以用于查询一个定长区间在以一定速度向后滑…...

deepseek为什么要开源

一、生态位的抢占与锁定&#xff1a;以 JDK 版本为例​ 在软件开发的世界里&#xff0c;生态位的抢占和先入为主的效应十分显著。就拿 Java 开发中的 JDK 版本来说&#xff0c;目前大多数开发者仍在广泛使用 JDK8。尽管 JDK17 和 JDK21 已经推出&#xff0c;且具备更多先进特性…...

MySQL基本建表操作

目录 1&#xff0c;创建数据库db_ck 1.1创建表 1.2 查看创建好的表 2,创建表t_hero 2.1 先进入数据库Db_Ck 2.1.1 这里可以看是否进入数据库: 2.2 创建表t_Hero 2.2.1 我们可以先在文本文档里面写好然后粘贴进去&#xff0c;因为直接写的话&#xff0c;错了要重新开始 …...

防火墙旁挂组网双机热备负载均衡

一&#xff0c;二层交换网络&#xff1a; 使用MSTPVRRP组网形式 VLAN 2--->SW3为主,SW4 作为备份 VLAN 3--->SW4为主,SW3 作为备份 MSTP 设计 --->SW3 、 4 、 5 运行 实例 1 &#xff1a; VLAN 2 实例 2 &#xff1a; VLAN 3 SW3 是实例 1 的主根&#xff0c;实…...

大白话react第十八章React 与 WebGL 项目的高级拓展与优化

大白话react第十八章React 与 WebGL 项目的高级拓展与优化 1. 实现 3D 模型的导入与动画 在之前的基础上&#xff0c;我们可以导入更复杂的 3D 模型&#xff0c;并且让这些模型动起来&#xff0c;就像在游戏里看到的角色和场景一样。这里我们使用 GLTF 格式的模型&#xff0c…...

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

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

C++:string容器(下篇)

1.string浅拷贝的问题 // 为了和标准库区分&#xff0c;此处使用String class String { public :/*String():_str(new char[1]){*_str \0;}*///String(const char* str "\0") // 错误示范//String(const char* str nullptr) // 错误示范String(const char* str …...

2.数据结构-栈和队列

数据结构-栈和队列 2.1栈2.1.1栈的表示和实现2.1.2栈的应用举例数制转换括号匹配检验迷宫给求解表达式求值 2.1.3链栈的表示和实现2.1.4栈与递归的实现遍历输出链表中各个结点的递归算法*Hanoi塔问题的递归算法 2.2队列2.2.1循环队列——队列的顺序表示和实现2.2.2链队——队列…...