【C++】运算符重载案例 - 字符串类 ② ( 重载 等号 = 运算符 | 重载 数组下标 [] 操作符 | 完整代码示例 )
文章目录
- 一、重载 等号 = 运算符
- 1、等号 = 运算符 与 拷贝构造函数
- 2、重载 等号 = 运算符 - 右操作数为 String 对象
- 3、不同的右操作数对应的 重载运算符函数
- 二、重载 下标 [] 运算符
- 三、完整代码示例
- 1、String.h 类头文件
- 2、String.cpp 类实现
- 3、Test.cpp 测试类
- 4、执行结果
一、重载 等号 = 运算符
1、等号 = 运算符 与 拷贝构造函数
等号 = 操作符 的 作用是 使用一个现有对象 为 另外一个现有对象赋值 ;
- 注意与 拷贝构造函数 区分 , 拷贝构造函数是 使用一个先有对象 为 一个新的对象进行初始化 ;
- 下面的代码中 , 分别调用 等号操作符 和 拷贝构造函数 ;
String s1, s2;
s1 = s2; // 这是使用 等号操作符 进行运算
String s3 = s2; // 这是使用 拷贝构造函数
2、重载 等号 = 运算符 - 右操作数为 String 对象
使用 成员函数 实现 重载 等号 = 运算符 :
- 首先 , 写出函数名 , 函数名规则为 " operate " 后面跟上要重载的运算符 ,
- 2 2 2 个对象
Student s1, s2
之间进行 等号运算 , 使用一个现有对象 为 另外一个现有对象赋值 ; - 使用时用法为
s1 = s2
; - 函数名是
operator=
;
- 2 2 2 个对象
operator=
- 然后 , 根据操作数 写出函数参数 , 参数一般都是 对象的引用 ;
- 等号运算符 使用时用法为
s1 = s2
; - 左操作数 : 其中 左操作数 是 s1 , 这里通过 this 指针调用 , 不需要声明在参数中 ;
- 右操作数 - 情况 ① : 右操作数 也是 String 对象 ; 该操作数需要声明在参数中 , 注意 普通数据类型 直接声明 , 对象数据类型 需要声明 为 引用类型 ;
- 上述两个是对象类型 , 对象一般传入 指针 或 引用 , 由于是基础数据类型 , 这里传入基础数据类型 ; 如果是 对象类型 , 则传入引用 ;
- 右操作数 - 情况 ② : 还有一种情况是
s1 = "Tom"
的用法 , 这样 右操作数 是 char* 类型的字符串 ;
- 等号运算符 使用时用法为
operator=(const String& s) // 传入 String 对象 , 使用 const 修饰参数 , 防止传入的对象被修改
operator=(const char* p) // 传入字符串值
- 再后 , 根据业务完善返回值 , 返回值可以是 引用 / 指针 / 元素 ;
- 此处返回值返回 String& 对象引用 , 可用于其它链式调用 ;
- 如果返回 String 对象 , 则是一次性的匿名对象 ;
String& operator=(const String& s) // 传入 String 对象 , 使用 const 修饰参数 , 防止传入的对象被修改
String& operator=(const char* p) // 传入字符串值
- 最后 , 实现函数体 , 编写具体的运算符操作业务逻辑 ;
- 先把本对象已分配的内存释放掉 ;
- 再进行赋值操作 ;
3、不同的右操作数对应的 重载运算符函数
不同的右操作数对应的 重载运算符函数 :
- 右操作数是 String 对象的情况 :
// 重载等号 = 操作符 , 右操作数是 String 对象的情况
String& String::operator=(const String& s)
{// 先处理本对象已分配的内存if (this->m_p != NULL){// 之前使用 new 分配的内存// 释放内存就需要使用 delete // 使用 malloc 分配的内存需要使用 free 释放delete[] this->m_p;// 设置指针指为空 , 避免出现野指针this->m_p = NULL;// 设置字符串长度为 0this->m_len = 0;}// 拷贝字符串长度// 注意 : 字符串指针 指向的内存空间大小需要 +1 , 内容是 '\0'this->m_len = s.m_len;// 使用 new 关键字为 char* m_p; 指针分配内存// 对于基础数据类型 new 等同于 mallocthis->m_p = new char[this->m_len + 1];// 拷贝字符串到 m_p 指向的内存中strcpy(this->m_p, s.m_p);cout << "调用重载等号 = 操作符函数 String& String::operator=(const String& s)" << endl;return *this;
}
- 右操作数为 字符串指针 的情况 :
// 重载等号 = 操作符 , 右操作数是 字符串常量值 的情况
String& String::operator=(const char* p)
{// 先处理本对象已分配的内存if (this->m_p != NULL){// 之前使用 new 分配的内存// 释放内存就需要使用 delete // 使用 malloc 分配的内存需要使用 free 释放delete[] this->m_p;// 设置指针指为空 , 避免出现野指针this->m_p = NULL;// 设置字符串长度为 0this->m_len = 0;}// 拷贝字符串长度// 注意 : 字符串指针 指向的内存空间大小需要 +1 , 内容是 '\0'this->m_len = strlen(p);// 使用 new 关键字为 char* m_p; 指针分配内存// 对于基础数据类型 new 等同于 mallocthis->m_p = new char[this->m_len + 1];// 拷贝字符串到 m_p 指向的内存中strcpy(this->m_p, p);cout << "调用重载等号 = 操作符函数 String& String::operator=(const char* p)" << endl;return *this;
}
二、重载 下标 [] 运算符
使用 成员函数 实现 重载 下标 [] 运算符 :
- 首先 , 写出函数名 , 函数名规则为 " operate " 后面跟上要重载的运算符 ,
- 下标 [] 运算符 , 使用时用法为
s[10]
; - 重载 下标 [] 运算符 函数名是
operator[]
;
- 下标 [] 运算符 , 使用时用法为
operator[]
- 然后 , 根据操作数 写出函数参数 , 参数一般都是 对象的引用 ;
- 下标 运算符 使用时用法为
s[10]
; - 左操作数 : 其中 左操作数 是 s 对象 , 这里通过 this 指针调用 , 不需要声明在参数中 ;
- 右操作数 : 右操作数 是 int 类型 索引值 ;
- 下标 运算符 使用时用法为
operator[](int i)
- 再后 , 根据业务完善返回值 , 返回值可以是 引用 / 指针 / 元素 ;
- 此处返回值 是 char 类型的 , 返回具体字符串指定索引的 char 类型字符 ;
- char 字符是内存中的一个地址中的值 , 这里返回引用类型 ;
char& operator[](int i)
- 最后 , 实现函数体 , 编写具体的运算符操作业务逻辑 ;
// 重载 数组下标 [] 操作符
char& String::operator[](int i)
{// 直接返回对应 i 索引字符return this->m_p[i];
}
三、完整代码示例
1、String.h 类头文件
#pragma once#include "iostream"
using namespace std;class String
{
public:// 默认的无参构造函数String();// 有参构造函数 , 接收一个 char* 类型字符串指针String(const char* p);// 拷贝构造函数 , 使用 String 对象初始化 对象值String(const String& s);// 析构函数~String();public:// 重载等号 = 操作符 , 右操作数是 String 对象的情况String& operator=(const String& s);// 重载等号 = 操作符 , 右操作数是 字符串常量值 的情况String& operator=(const char* p);// 重载 数组下标 [] 操作符char& operator[](int i);private:// 字符串长度 , 不包括 '\0'// 内存占用空间大小 = 字符串长度 + 1int m_len;// 字符串指针, 指向堆内存中的字符串char* m_p;
};
2、String.cpp 类实现
// 使用 strcpy 函数报错
// error C4996: 'strcpy': This function or variable may be unsafe.
// Consider using strcpy_s instead.
// To disable deprecation, use _CRT_SECURE_NO_WARNINGS.
// See online help for details.
#define _CRT_SECURE_NO_WARNINGS#include "String.h"// 默认的无参构造函数
String::String()
{// 默认构造一个空字符串 , 字符串长度为 0 // 但是 , 字符串指针 指向的内存空间大小是 1 , 内容是 '\0'm_len = 0;// 使用 new 关键字为 char* m_p; 指针分配内存// 对于基础数据类型 new 等同于 mallocm_p = new char[m_len + 1];// 拷贝空字符串到 m_p 指向的内存中strcpy(m_p, "");cout << "调用无参构造函数" << endl;
}// 有参构造函数 , 接收一个 char* 类型字符串指针
String::String(const char* p)
{if (p == NULL){// 默认构造一个空字符串 , 字符串长度为 0 // 但是 , 字符串指针 指向的内存空间大小是 1 , 内容是 '\0'this->m_len = 0;// 使用 new 关键字为 char* m_p; 指针分配内存// 对于基础数据类型 new 等同于 mallocthis->m_p = new char[this->m_len + 1];// 拷贝空字符串到 m_p 指向的内存中strcpy(m_p, "");}else{// 获取传入字符串的长度// 但是 , 字符串指针 指向的内存空间大小需要 +1 , 内容是 '\0'this->m_len = strlen(p);// 使用 new 关键字为 char* m_p; 指针分配内存// 对于基础数据类型 new 等同于 mallocthis->m_p = new char[this->m_len + 1];// 拷贝字符串到 m_p 指向的内存中strcpy(m_p, p);}cout << "调用有参构造函数" << endl;
};// 拷贝构造函数 , 使用 String 对象初始化 对象值
String::String(const String& s)
{// 拷贝字符串长度// 注意 : 字符串指针 指向的内存空间大小需要 +1 , 内容是 '\0'this->m_len = s.m_len;// 使用 new 关键字为 char* m_p; 指针分配内存// 对于基础数据类型 new 等同于 mallocthis->m_p = new char[this->m_len + 1];// 拷贝字符串到 m_p 指向的内存中strcpy(this->m_p, s.m_p);cout << "调用拷贝构造函数" << endl;
}// 析构函数
String::~String()
{if (this->m_p != NULL){// 之前使用 new 分配的内存// 释放内存就需要使用 delete // 使用 malloc 分配的内存需要使用 free 释放delete[] this->m_p;// 设置指针指为空 , 避免出现野指针this->m_p = NULL;// 设置字符串长度为 0this->m_len = 0;}
}// 重载等号 = 操作符 , 右操作数是 String 对象的情况
String& String::operator=(const String& s)
{// 先处理本对象已分配的内存if (this->m_p != NULL){// 之前使用 new 分配的内存// 释放内存就需要使用 delete // 使用 malloc 分配的内存需要使用 free 释放delete[] this->m_p;// 设置指针指为空 , 避免出现野指针this->m_p = NULL;// 设置字符串长度为 0this->m_len = 0;}// 拷贝字符串长度// 注意 : 字符串指针 指向的内存空间大小需要 +1 , 内容是 '\0'this->m_len = s.m_len;// 使用 new 关键字为 char* m_p; 指针分配内存// 对于基础数据类型 new 等同于 mallocthis->m_p = new char[this->m_len + 1];// 拷贝字符串到 m_p 指向的内存中strcpy(this->m_p, s.m_p);cout << "调用重载 等号 = 操作符函数 String& String::operator=(const String& s)" << endl;return *this;
}// 重载等号 = 操作符 , 右操作数是 字符串常量值 的情况
String& String::operator=(const char* p)
{// 先处理本对象已分配的内存if (this->m_p != NULL){// 之前使用 new 分配的内存// 释放内存就需要使用 delete // 使用 malloc 分配的内存需要使用 free 释放delete[] this->m_p;// 设置指针指为空 , 避免出现野指针this->m_p = NULL;// 设置字符串长度为 0this->m_len = 0;}// 拷贝字符串长度// 注意 : 字符串指针 指向的内存空间大小需要 +1 , 内容是 '\0'this->m_len = strlen(p);// 使用 new 关键字为 char* m_p; 指针分配内存// 对于基础数据类型 new 等同于 mallocthis->m_p = new char[this->m_len + 1];// 拷贝字符串到 m_p 指向的内存中strcpy(this->m_p, p);cout << "调用重载 等号 = 操作符函数 String& String::operator=(const char* p)" << endl;return *this;
}// 重载 数组下标 [] 操作符
char& String::operator[](int i)
{cout << "调用重载 下标 [] 操作符函数 char& String::operator[](int i)" << endl;// 直接返回对应 i 索引字符return this->m_p[i];
}
3、Test.cpp 测试类
#include "iostream"
using namespace std;// 导入自定义的 String 类
#include "String.h"int main() {// 调用无参构造函数String s1;// 调用有参构造函数String s2("Tom");// 调用拷贝构造函数String s3 = s2;// 调用重载的等号运算符函数, 右操作数是 String 对象s1 = s2;// 调用重载的等号运算符函数, 右操作数是 字符串常量值 , char* 指针类型s3 = "Jerry";// 调用重载的下标运算符函数char c = s3[3];// 控制台暂停 , 按任意键继续向后执行system("pause");return 0;
}
4、执行结果
执行结果 :
调用无参构造函数
调用有参构造函数
调用拷贝构造函数
调用重载 等号 = 操作符函数 String& String::operator=(const String& s)
调用重载 等号 = 操作符函数 String& String::operator=(const char* p)
调用重载 下标 [] 操作符函数 char& String::operator[](int i)
相关文章:

【C++】运算符重载案例 - 字符串类 ② ( 重载 等号 = 运算符 | 重载 数组下标 [] 操作符 | 完整代码示例 )
文章目录 一、重载 等号 运算符1、等号 运算符 与 拷贝构造函数2、重载 等号 运算符 - 右操作数为 String 对象3、不同的右操作数对应的 重载运算符函数 二、重载 下标 [] 运算符三、完整代码示例1、String.h 类头文件2、String.cpp 类实现3、Test.cpp 测试类4、执行结果 一…...

Vue脚手架开发流程
一、项目运行时会先执行 public / index.html 文件 <!DOCTYPE html> <html lang""><head><meta charset"utf-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport&quo…...

从零开始学习线性回归:理论、实践与PyTorch实现
文章目录 🥦介绍🥦基本知识🥦代码实现🥦完整代码🥦总结 🥦介绍 线性回归是统计学和机器学习中最简单而强大的算法之一,用于建模和预测连续性数值输出与输入特征之间的关系。本博客将深入探讨线性…...

[LeetCode]链式二叉树相关题目(c语言实现)
文章目录 LeetCode965. 单值二叉树LeetCode100. 相同的树LeetCode101. 对称二叉树LeetCode144. 二叉树的前序遍历LeetCode94. 二叉树的中序遍历LeetCode145. 二叉树的后序遍历LeetCode572. 另一棵树的子树 LeetCode965. 单值二叉树 题目 Oj链接 思路 一棵树的所有值都是一个…...

集成学习
集成学习(Ensemble Learning) - 知乎 (zhihu.com)https://zhuanlan.zhihu.com/p/27689464集成学习就是组合这里的多个弱监督模型以期得到一个更好更全面的强监督模型,集成学习潜在的思想是即便某一个弱分类器得到了错误的预测,其他的弱分类器…...

算法练习11——买卖股票的最佳时机 II
LeetCode 122 买卖股票的最佳时机 II 给你一个整数数组 prices ,其中 prices[i] 表示某支股票第 i 天的价格。 在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买,然后在 同一天 出售。 返回…...

linux——多线程,线程控制
目录 一.POSIX线程库 二.线程创建 1.创建线程接口 2.查看线程 3.多线程的健壮性问题 4.线程函数参数传递 5.线程id和地址空间 三.线程终止 1.pthread_exit 2.pthread_cancel 四.线程等待 五.线程分离 一.POSIX线程库 站在内核的角度,OS只有轻量级进程…...

Oracle 简介与 Docker Compose部署
最近,我翻阅了在之前公司工作时的笔记,偶然发现了一些有关数据库的记录。当初,我们的项目一开始采用的是 Oracle 数据库,但随着项目需求的变化,我们不得不转向使用 SQL Server。值得一提的是,公司之前采用的…...

mp4音视频分离技术
文章目录 问题描述一、分离MP3二、分离无声音的MP4三、结果 问题描述 MP4视频想拆分成一个MP3音频和一个无声音的MP4文件 一、分离MP3 ffmpeg -i C:\Users\Administrator\Desktop\一个文件夹\我在财神殿里长跪不起_完整版MV.mp4 -vn C:\Users\Administrator\Desktop\一个文件…...

JVM 参数
JVM 参数类型大致分为以下几类: 标准参数(-):保证在所有的 JVM 实现都支持的参数非标准参数(-X):通用的,特定于 HotSpot 虚拟机的参数,这些参数不保证在所有 JVM 实现中…...

黑马点评-07缓存击穿问题(热点key失效)及解决方案,互斥锁和设置逻辑过期时间
缓存击穿问题(热点key失效) 缓存击穿问题也叫热点Key问题,就是一个被高并发访问并且重建缓存业务较复杂的key突然失效了,此时无数的请求访问会在瞬间打到数据库,带来巨大的冲击 一件秒杀中的商品的key突然失效了,由于大家都在疯狂抢购那么这个瞬间就会有无数的请求…...

信息系统项目管理师第四版学习笔记——项目进度管理
项目进度管理过程 项目进度管理过程包括:规划进度管理、定义活动、排列活动顺序、估算活动持续时间、制订进度计划、控制进度。 规划进度管理 规划进度管理是为规划、编制、管理、执行和控制项目进度而制定政策、程序和文档的过程。本过程的主要作用是为如何在…...

指挥棒:C++ 与运算符
文章目录 参考描述算术运算符除法运算取模运算复合赋值运算符自增运算符自减运算符 比较运算符逻辑运算符概念短路为什么需要短路机制? 参考 项目描述微软C 语言文档搜索引擎Bing、GoogleAI 大模型文心一言、通义千问、讯飞星火认知大模型、ChatGPTC Primer Plus &…...

HTTPS建立连接的过程
HTTPS 协议是基于 TCP 协议的,因而要先建立 TCP 的连接。在这个例子中,TCP 的连接是在手机上的 App 和负载均衡器 SLB 之间的。 尽管中间要经过很多的路由器和交换机,但是 TCP 的连接是端到端的。TCP 这一层和更上层的 HTTPS 无法看到中间的包…...

Python接口自动化搭建过程,含request请求封装!
开篇碎碎念 接口测试自动化好处 显而易见的好处就是解放双手😀。 可以在短时间内自动执行大量的测试用例通过参数化和数据驱动的方式进行测试数据的变化,提高测试覆盖范围快速反馈测试执行结果和报告支持持续集成和持续交付的流程 使用Requestspytes…...

Vue3 编译原理
文章目录 一、编译流程1. 解读入口文件 packgages/vue/index.ts2. compile函数的运行流程 二、AST 解析器1. ast 的生成2. 创建ast的根节点3. 解析子节点 parseChildren(关键)4. 解析模版元素 Element模版元素解析-举例分析 一、编译流程 1. 解读入口文…...

spring boot整合Minio
MinIO 安装MinIo # 先创建minio 文件存放的位置 mkdir -p /opt/docker/minio/data# 启动并指定端口 docker run \-p 9000:9000 \-p 5001:5001 \--name minio \-v /opt/docker/minio/data:/data \-e "MINIO_ROOT_USERminioadmin" \-e "MINIO_ROOT_PASSWORDmini…...

Hadoop----Azkaban的使用与一些报错问题的解决
1.因为官方只放出源码,并没有放出其tar包,所以需要我们自己编译,通过查阅资料我们可以使用gradlew对其进行编译,还是比较简单,然后将里面需要用到的服务文件夹进行拷贝,完善其文件夹结构,通常会…...

「新房家装经验」客厅电视高度标准尺寸及客厅电视机买多大尺寸合适?
客厅电视悬挂高度标准尺寸是多少? 客厅电视悬挂高度通常在90~120厘米之间,电视挂墙高度也可以根据个人的喜好和实际情况来调整,但通常不宜过高,以坐在沙发上观看时眼睛能够平视到电视中心点或者中心稍微往下一点的位置为适宜。 客…...

ArduPilot开源飞控之AP_Baro_DroneCAN
ArduPilot开源飞控之AP_Baro_DroneCAN 1. 源由2. back-end抽象类3. 方法实现3.1 probe3.2 update3.3 subscribe_msgs3.4 handle_pressure/handle_temperature3.5 CAN port 4. 参考资料 1. 源由 鉴于ArduPilot开源飞控之AP_Baro中涉及Sensor Driver有以下总线类型: …...

Supervised Contrastive Pre-training for Mammographic Triage Screening Model
方法 品红色箭头表示将生成的孪生编码器分别迁移到单视角学习模块和双视角学习模块...

JVM技术文档--JVM优化思路以及问题定位--JVM可调整参数汇总
阿丹: 一个优秀的程序员,是因为在线上的排查以及遇到的线上、生产事故较多所以定位问题以及解决问题会比普通程序员快很多,所以一个优秀的程序员要逐渐形成自己的方法论,来完善和解决问题。 我们是如何发现问题的呢? …...

Oracle10g数据库迁移方案
试验了很多次Oracle数据库迁移才成功,贴出来给大家参考一下,我看到有的地方写迁移之后还需要重新建立temp表空间,这个还没有研究。另外说一点的是两个数据库的版本一定要一致,之前失败过一次,就是因为两个数据库的版本…...

备忘录模式:对象状态的保存与恢复
欢迎来到设计模式系列的第十八篇文章,本篇将介绍备忘录模式。备忘录模式是一种行为型设计模式,它允许在不破坏封装性的前提下捕获一个对象的内部状态,并在之后恢复该状态。这种模式通常用于需要提供撤销操作的情况。 什么是备忘录模式&#…...

C# InvokeRequired线程安全
C# InvokeRequired线程安全 为了保证新家的线程可能要对主界面的控件元素的属性发生一些改变,此时防止此操作对于主线程的影响,就提出了 InvokeRequired方法,保证主线程的安全,同时新加的线程也可以改变主页面中元素的值。 定义…...

pdf怎么转成jpg图片格式
pdf怎么转成jpg图片格式?对于大家平时在工作或者生活中的图片使用习惯,经常需要将各种格式的文件转换成易于浏览和使用的JPG格式图片以便保存。如今,因为pdf文件具有更强的稳定性和设备兼容性,PDF文件在平时的电脑使用过程中可以说…...

React +ts + babel+webpack
babel babel/preset-typescript 专门处理ts "babel/cli": "^7.17.6", "babel/core": "^7.17.8", "babel/preset-env": "^7.16.11", "babel/preset-react": "^7.16.7", "babel/preset…...

红队专题-REVERSE二进制逆向反编译
红队专题 招募六边形战士队员IDA pro安装python2加入环境变量py2安装pip安装IDA 7.0 proIDAPython: importing "site" failed. 招募六边形战士队员 一起学习 代码审计、安全开发、web攻防、逆向等。。。 私信联系 IDA pro 安装python2 python-2.7.3.msi 加入环…...

Spring技术原理之Bean生命周期原理解析
Spring技术原理之Bean生命周期原理解析 Spring作为Java领域中的优秀框架,其核心功能之一是依赖注入和生命周期管理。其中,Bean的生命周期管理是Spring框架中一个重要的概念。在本篇文章中,我们将深入探讨Spring技术原理中的Bean生命周期原理…...

Unity实现设计模式——模板方法模式
Unity实现设计模式——模板方法模式 模板模式(Template Pattern), 指在一个抽象类公开定义了执行它的方法的模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。 简单说, 模板方法模式定义一个操作中的算法的骨架&…...