【C++】运算符重载案例 - 字符串类 ② ( 重载 等号 = 运算符 | 重载 数组下标 [] 操作符 | 完整代码示例 )

news2024/11/17 21:18:02

文章目录

  • 一、重载 等号 = 运算符
    • 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= ;
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;

		// 设置字符串长度为 0
		this->m_len = 0;
	}

	// 拷贝字符串长度
	// 注意 : 字符串指针 指向的内存空间大小需要 +1 , 内容是 '\0'
	this->m_len = s.m_len;

	// 使用 new 关键字为 char* m_p; 指针分配内存
	// 对于基础数据类型 new 等同于 malloc
	this->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;

		// 设置字符串长度为 0
		this->m_len = 0;
	}

	// 拷贝字符串长度
	// 注意 : 字符串指针 指向的内存空间大小需要 +1 , 内容是 '\0'
	this->m_len = strlen(p);

	// 使用 new 关键字为 char* m_p; 指针分配内存
	// 对于基础数据类型 new 等同于 malloc
	this->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'
	// 内存占用空间大小 = 字符串长度 + 1
	int 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 等同于 malloc
	m_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 等同于 malloc
		this->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 等同于 malloc
		this->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 等同于 malloc
	this->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;

		// 设置字符串长度为 0
		this->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;

		// 设置字符串长度为 0
		this->m_len = 0;
	}

	// 拷贝字符串长度
	// 注意 : 字符串指针 指向的内存空间大小需要 +1 , 内容是 '\0'
	this->m_len = s.m_len;

	// 使用 new 关键字为 char* m_p; 指针分配内存
	// 对于基础数据类型 new 等同于 malloc
	this->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;

		// 设置字符串长度为 0
		this->m_len = 0;
	}

	// 拷贝字符串长度
	// 注意 : 字符串指针 指向的内存空间大小需要 +1 , 内容是 '\0'
	this->m_len = strlen(p);

	// 使用 new 关键字为 char* m_p; 指针分配内存
	// 对于基础数据类型 new 等同于 malloc
	this->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)

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

多线程(线程互斥)

抢票代码编写 学习了前面有关线程库的操作后&#xff0c;我们就可以模拟抢票的过程 假设我们创建四个线程&#xff0c;分别代表我们的用户 然后设定总票数为1000张&#xff0c;四个线程分别将进行循环抢票操作&#xff0c;其实就是循环对票数进行打印&#xff0c;并进行对应的…

【小记】Java将字符串中的EL表达式动态转换为Map中的值

项目中碰到了这样的问题&#xff0c;例如数据库中有一个字符串模版如下&#xff1a; ${userNamme}开启了一个新的${project}我们要替换里面的${}表达式&#xff0c;来实现动态的生成完整的信息~实现方式很简单如下&#xff1a; 引入pom依赖&#xff1a; <dependency>&l…

【附代码】使用Shapely计算多边形外扩与收缩

文章目录 相关文献效果图代码 作者&#xff1a;小猪快跑 基础数学&计算数学&#xff0c;从事优化领域5年&#xff0c;主要研究方向&#xff1a;MIP求解器、整数规划、随机规划、智能优化算法 本文档介绍如何使用 Shapely Python 包 计算多边形外扩与收缩。 如有错误&…

软件测试面试,5年测试工程师竟被面试官10分钟pass,这些问题一定要注意啊!

随着软件测试领域对于技术要求越来越清晰&#xff0c;到现在测试人员在市场上的岗位需求也变得越来越复杂。极大部分的企业都开始对自动化测试岗位有了更多的需要。自然而然&#xff0c;面试就相对于非常重要了。 网络上有着各式各样的测试框架的存在&#xff0c;我也不知道我…

Mall脚手架总结(三) —— MongoDB存储浏览数据

前言 通过Elasticsearch整合章节的学习&#xff0c;我们了解SpringData框架以及相应的衍生查询的方式操作数据读写的语法。MongoDB的相关操作也同样是借助Spring Data框架&#xff0c;因此这篇文章的内容比较简单&#xff0c;重点还是弄清楚MongoDB的使用场景以及如何通过Sprin…

LeetCode862 和至少为k的最短子数组

题目&#xff1a; 解析&#xff1a; 1、先构造前缀和数组 2、单调队列存放滑动窗口&#xff0c;目的求Sj-Si >k的情况下&#xff0c;窗口最小。 代码&#xff1a; class Solution {public int shortestSubarray(int[] nums, int k) {int n nums.length;long[] sums new …

网工内推 | base郑州,上市公司,最高15薪,五险一金全额缴

01 四方达 招聘岗位&#xff1a;网络工程师 职责描述&#xff1a; 1、负责公司数据中心&#xff08;机房&#xff09;的管理与运维工作。 2、负责公司服务器、路由器、防火墙、交换机等设备的管理、以及网络平台的运行监控和维护&#xff1b; 3、负责公司服务器运维管理工作、…

Git相关知识(1)

目录 1.初识Git 1.基础知识 2.centos中下载 2.基本操作 1.创建本地仓库 2.配置本地仓库 3.版本库、工作区、暂存区 4.添加文件 5.add和commit对git文件的作用 6.修改文件 7.版本回退 8.撤销修改 9.删除文件 3.分支操作 1.HEAD与分支 2.创建分支 3.删除分支 …

python中TagMe包的token获取

草&#xff0c;找了40分钟&#xff01;帮助大家少浪费时间。 1.注册Tagme&#xff0c;注册地址https://services.d4science.org/home &#xff08;我用邮箱注册一直说验证码不对&#xff0c;最后用Google账号注册的&#xff09; 2.找sobigdata 3.进入sobigdata后&#xff0c;…

17基于matlab卡尔曼滤波的行人跟踪算法,并给出算法估计误差结果,判断算法的跟踪精确性,程序已调通,可直接运行,基于MATLAB平台,可直接拍下。

17基于matlab卡尔曼滤波的行人跟踪算法&#xff0c;并给出算法估计误差结果&#xff0c;判断算法的跟踪精确性&#xff0c;程序已调通&#xff0c;可直接运行&#xff0c;基于MATLAB平台&#xff0c;可直接拍下。 17matlab卡尔曼滤波行人跟踪 (xiaohongshu.com)

MySQL总结练习题

目录 1.准备数据表 2.表之间的关系 3.题目 3.1 取得每个部门最高薪水的人员名称 3.2 哪些人的薪水在部门的平均薪水之上 3.3 取得部门中&#xff08;所有人的&#xff09;平均的薪水等级 3.4 不准用组函数&#xff08;Max &#xff09;&#xff0c;取得最高薪水 3.5 取…

Java基础之反射机制

背景&#xff1a;Java程序中的所有对一项都有两种类型&#xff1a;编译时类型和运行时类型&#xff08;由于多态导致的&#xff09;&#xff0c;这可能会导致对象的编译时类型和运行时类型不一致。 反射&#xff08;Reflection&#xff09;是被是为动态语言的关键&#xff0c;反…

速通Redis基础(二):掌握Redis的哈希类型和命令

目录 Redis 哈希类型简介 Redis 哈希命令 HSET HGET HEXISTS HDEL HKEYS HVALS HGETALL HMGET HLEN HSETNX ​编辑 HINCRBY HINCRBYFLOAT Redis的哈希类型命令小结 Redis 是一种高性能的键值存储数据库&#xff0c;支持多种数据类型&#xff0c;其中之…

jdbc+数据连接池

1.1 JDBC概念 JDBC 就是使用Java连接并操作数据库的一套API 全称&#xff1a;( Java DataBase Connectivity ) Java 数据库连接 1.2 JDBC优势 可随时替换底层数据库&#xff0c;访问数据库的Java代码基本不变 以后编写操作数据库的代码只需要面向JDBC&#xff08;接口&…

如何在Windows系统搭建VisualSVN服务并在公网远程访问【内网穿透】

文章目录 前言1. VisualSVN安装与配置2. VisualSVN Server管理界面配置3. 安装cpolar内网穿透3.1 注册账号3.2 下载cpolar客户端3.3 登录cpolar web ui管理界面3.4 创建公网地址 4. 固定公网地址访问 前言 SVN 是 subversion 的缩写&#xff0c;是一个开放源代码的版本控制系统…

尚品甄选2023全新SpringBoot+SpringCloud企业级微服务项目

最适合新手入门的SpringBootSpringCloud企业级微服务项目来啦&#xff01;如果你已经学习了Java基础、SSM框架、SpringBoot、SpringCloud&#xff0c;想找一个项目来实战练习&#xff1b;或者你刚刚入行&#xff0c;需要可以写到简历中的微服务架构项目&#xff01; 项目采用前…

大运新能源天津车展深度诠释品牌魅力 为都市人群打造理想车型

如今&#xff0c;新能源汽车行业发展潜力巨大&#xff0c;不断吸引无数车企入驻新能源汽车赛道&#xff0c;而赛道的持续紧缩也让一部分车企很难找到突破重围的机会。秉持几十年的造车经验&#xff0c;大运新能源凭借雄厚的品牌实力从一众车企中脱颖而出。从摩托车到重卡&#…

在两个有序数组中找整体第k小的数

一、题目 给定两个已经排序的数组&#xff08;假设按照升序排列&#xff09;&#xff0c;然后找出第K小的数。比如数组A {1&#xff0c; 8&#xff0c; 10&#xff0c; 20}&#xff0c; B {5&#xff0c; 9&#xff0c; 22&#xff0c; 110}&#xff0c; 第 3 小的数是 8.。…

基于 Mtcnn(人脸检测)+Hopenet(姿态检测)+拉普拉斯算子(模糊度检测) 的人脸检测服务

写在前面 工作原因&#xff0c;顺便整理博文内容为一个 人脸检测服务分享以打包 Docker 镜像&#xff0c;可以直接使用服务目前支持 http 方式该检测器主要适用低质量人脸图片处理理解不足小伙伴帮忙指正&#xff0c;多交流&#xff0c;相互学习 对每个人而言&#xff0c;真正的…

11.动名词

一.什么是动名词 动名词是动词的另一种非谓语动词形式。动名词跟宾语或状语构成动名词短语&#xff0c;动名词跟动词不定式一样&#xff0c;都属于非谓语动词&#xff0c;不能作谓语&#xff0c;动名词或动名词短语可以作主语&#xff0c;表语&#xff0c;宾语&#xff0c;介词…