learn C++ NO.11——string类模拟实现

news2024/11/15 13:43:31

前言

本篇文章主要是讲string类的模拟实现,模拟实现的是string类的常用接口以及成员函数。让读者对string类有更深的理解。适当的“造轮子”,有助于我们的语言学习。

简单描述string类

string类其实就是一个管理字符数组的线性表,我们可以使用string头文件内提供的接口来对string类进行数据的增删查改。

第一部分:初步搭建一个string类最基本的框架

在这里插入图片描述
我们以上面图中的一个简单的程序为模拟实现的第一个部分。首先,我们需要定义一个string类。然后,定义string类的成员变量。提供构造函数和析构函数。最后,提供一个c_str接口能够是<<运算符能够提取出模拟实现的string类的数据。

定义成员变量

我们将要实现的string类实现的成员变量如下,定义一个char类型指针的成员变量_str,来保存在堆上动态开辟的内存空间。分别定义两个无符号整型成员变量_size和_capacity,顾名思义_size用于记录string类对象的有效数据个数,_capacity用于记录string类对象的在堆上开辟空间的大小。需要将成员变量私有,以防止在类域外面能够访问修改数据。仅提供对应接口让用户能够访问成员变量的内容。
在这里插入图片描述

构造函数的模拟实现

这里我们实现的是通过一个c字符串的内容来构造一个string类的全缺省构造函数。它同样可以完成string类的默认构造的对应功能。
实现思路: 首先,我们先通过strlen()求出需要形参部分的字符串的长度,并将结果保存到一个临时变量中。然后,new一块长度为形参部分长度的空间+1赋值给_str(这里+1是为了放’\0’)。将形参部分的字符串的长度赋值给_size和_capacity。最后,在函数体内直接通过c库函数strcpy来将形参的内容拷贝给_str。
在这里插入图片描述
这里我们在默认参数中给的是一个空串,其实这里是一个很妙的处理。c语言中规定""引起的字符串内容中包含一个隐藏的’\0’。当我们不传参数时,我们还是会开辟一个字节的空间存放’\0’字符来初始化string类对象。

析构函数的模拟实现

因为string类的数据需要存放在堆区上,所以析构函数需要我们实现,编译器默认生成的析构函数无法完成对堆区资源的释放。只需要将堆区空间释放并处理_size和_capacity即可。
在这里插入图片描述

c_str()函数的模拟实现

在这里插入图片描述
c_str()函数是获取string类的内筒,以c字符串形式返回。简而言之,c_str()接口返回的是成员变量_str。需要注意的是需要使用const来修饰一下返回值,以保护成员变量_str。
在这里插入图片描述

至此第一部分的最最基本的string类的框架就搭建好了,下面我们再稍微完善完善模拟实现的string类。

第二部分:进一步完善功能

size()函数的模拟实现和capacity()的模拟实现

size()函数用于获取string类有效元素的个数,本质其实就是返回_size的值。capacity()函数用于获取string类在堆区开辟的空间的长度,返回的是成员变量_capacity。

在这里插入图片描述

[]运算符重载的模拟实现

由于库里string类是重载了操作符[],使得string类像数组一样。这极大程度上方便用户对于string类的某一个位置的数据进行增删查改。
实现思路: 实现思路就是返回_str的某一个位置的字符。需要注意的是库里面对于下标有效性的检查是assert的暴力检查。
在这里插入图片描述

迭代器及相关成员函数模拟实现

string类的迭代器其实就是一个类似于指针的一个东西,本质是其成员函数_str,但是迭代器不一定都是指针。例如list,其迭代器就不是指针,具体细节等模拟实现list时再说。begin()其实就是返回string类对象第一个元素的地址,end()返回的是string类对象有效字符的下一个地址('\0’的地址)。

在这里插入图片描述

在这里插入图片描述
有了迭代器就可以支持范围for语法。因为范围for的底层是通过迭代器来实现的。这里就简单做个演示。
在这里插入图片描述

reserve()函数的模拟实现

reserve函数用于将_str在堆区申请的空间大小进行调整。
实现思路:当调整的值 > _capacity时就进行扩容,当n<_capacity不做处理。开辟一段长度为n的堆区空间,将_str的值拷贝给新开辟的空间(需要拷贝’\0’),随后释放_str。最后将tmp赋值给_str,将_capacity修改成n。
在这里插入图片描述

resize(size_t n, char ch = ‘\0’)函数的模拟实现

resize()函数用于调整string类对象有效字符的长度至指定的长度,也支持指定字符来进行初始化。
在这里插入图片描述
实现思路:当n小于当前string对象有效长度时,将n赋值给_size,有效长度的后一个位置添上’\0’。当n大于string对象有效长度时,先用reserve来调整有效的空间大小,然后从_size后开始写入字符ch直到有效长度达到n。修改_size的值为n。
在这里插入图片描述

push_back()函数 的模拟实现

push_back()函数就是在string类对象的有效长度的下一位插入元素。这里以插入一个字符的版本为例进行模拟实现。
实现思路:先判断空间是否足够,若不够就扩容。然后在第_size个位置插入字符。随后++_size,并在_size下标位置上放上’\0’。
在这里插入图片描述

append()函数的模拟实现

append()函数就是在string类对象的有效长度后追加数据,这里以常用的追加一个c字符串为例模拟实现。
实现思路:首先,判断堆区空间是否足够。然后,在_size下标出,将c字符串拷贝c字符串长度个数据到_size后的空间。最后更新string类对象的有效长度。
在这里插入图片描述

operator += 重载的模拟实现

由于上面我们已经实现了push_back()和append()接口,这里我们至今进行一波复用就可以啦。
在这里插入图片描述

insert()函数的模拟实现

这里我们分别模拟实现两个版本,一个是在pos位置插入n个字符,另一个是在pos位置插入一个c字符串。再此之前我要引入一个新的成员变量npos,他是一个静态的公用成员变量,它表示的是最大的无符号整型值,即-1(2^32-1)。

实现思路:首先,判断一下pos下标的合法性。然后,判断堆区空间是否足够,不够就扩容。接下来,从pos位置开始向后挪动数据(需要注意边界控制),挪动n位。在pos位置插入n个字符或者一个c字符串。最后,更新一下_size。
在这里插入图片描述

find()函数的模拟实现

find接口用于从pos下标处开始查找一个字符或者一个字符串在当前string类对象的下标,没有找到则返回npos,所以我们还是实现两个版本。
实现思路:先判断下标的合法性。字符串版本进行strstr匹配,成功找到的话返回strstr的返回值-_str的偏移量,没有找到则返回npos。字符版本,直接使用一个for循环遍历string类对象,找到了就返回对应下标,否则返回npos。
在这里插入图片描述

erase()函数的模拟实现

erase函数用于删除从pos下标处开始,删除长度为len个数据。
实现思路:首先,判断pos下标的合法性。然后,根据len来判断删除的方式,若len = npos或者pos + len > _size,即从pos位置开始直接删完。若pos + len < _size,则需要挪动数据覆盖。
在这里插入图片描述

两个string类比较运算符重载的实现

这里就重点讲 重载< 和 重载== 的实现。只要实现这两个运算符,其余的比较运算符只需要复用它们俩就行。
实现思路:先讲 < 重载的实现。我们先找到两个string类有效长度短的那一个作memcmp的num参数。然后,根据memcmp的返回值进行判断。若返回值等于0,只需要判断_size是否小于s._size。若小于返回真,否则返回假。其他情况下,ret只要小于0,就返回真,否则返回假。而 == 重载需要我们先比较两个string类对象的有效长度是否一样,不一样直接返回假。一样的情况下,再进行memcmp,返回值为0才为真,否则为假。

在这里插入图片描述
其余情况复用上面两个运算符重载函数即可。
在这里插入图片描述

流插入运算符重载的模拟实现

实现思路:通过循环将string类对象的内容一个字符一个字符的流入到ostream类对象中,最后返回这个类对象。
在这里插入图片描述

流提取运算符重载的模拟实现

实现思路:第一步,需要清空string类的数据。紧接着,将缓冲区的空格和换行给清掉。我们定义一个buffer数组来存放从键盘上输入的字符,这样可以减少扩容所带来的性能消耗。然后将buffer追加到string类对象中即可。需要注意的是由于io流里面默认是以空格和换行进行分割的。所有在模拟实现流提取运算符重载时,我们需要用到一个接口就是get()。

在这里插入图片描述

深浅拷贝

浅拷贝的介绍

首先,下面请看一段代码
在这里插入图片描述
这里由于我们没有实现拷贝构造,编译器默认生成的拷贝构造导致了同一块内存被析构函数释放了两次。为什么呢?因为编译器默认生成的拷贝构造是浅拷贝,即值拷贝。其实就是将s1的三个成员变量的内容直接赋值给s2。main函数生命周期即将结束时,调用析构函数清理动态申请内存时,同一块动态申请的内存被释放了两次,导致的程序错误。此时,就需要使用深拷贝进行解决此问题。

深拷贝的介绍

深拷贝是指在拷贝对象时,不仅拷贝对象的值,还要拷贝对象所指向的内存空间,即重新分配一块内存空间,并将原对象的值复制到新的内存空间中。这样,原对象和拷贝对象就互不干扰,修改一个对象的值不会影响另一个对象的值。深拷贝通常需要自定义拷贝构造函数和赋值运算符重载函数来实现。

在这里插入图片描述

拷贝构造的模拟实现

这里我依次介绍传统写法以及现代写法的拷贝构造。
传统写法实现思路:先开辟根据被拷贝对象的大小申请空间并给_str,随后将被拷贝对象的数据内容拷贝到_str中,最后将被拷贝对象的_size和_capacity的值赋值给拷贝对象的_size和_capacity中。
在这里插入图片描述
现代写法实现思路: 先定义一个局部string对象tmp并用被拷贝的string类对象的内容去构造他,然后调用swap函数来将tmp对象的内容交换给拷贝对象。随后除了作用域tmp对象会被析构。就完成了拷贝构造的现代写法。这就像你假期在家时,让你的弟弟妹妹给你当工具人,吃饭让他们给你端进房间,吃完了他们还给你把碗筷收拾干净了端走。这里的tmp临时对象起到的就是这个作用。
在这里插入图片描述

operator = 重载的模拟实现

其实有了上面现代写法为基础,operator = 的实现思路大致也是一致,利用swap将形参的临时对象tmp内容交换给我们的拷贝对象即可。
在这里插入图片描述

substr()函数的模拟实现

有了对深浅拷贝的初步理解,接下来就带大家模拟实现一个常用的string类的接口substr()。substr()其实就是在当前string类对象的pos下标处构造一个长度为len的子串。
实现思路:首先,需要判断pos位置的有效性。然后,定义一个临时对象tmp。判断一下需要构造的子串的长度并修正。提前给tmp开辟好合适的容量,直接拷贝数据给tmp。最后,返回临时对象tmp。
在这里插入图片描述

总结

对string类的模拟实现主要是为了能够让我们更进一步的掌握string类。会使用string类和掌握string类底层逻辑,两者还是有很大的区别的。知其然,知其所以然。在学习语言时,适当的”造轮子“可以让你的水平更进一步。点击获取完整代码

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

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

相关文章

常见面试题-Redis专栏(二)

theme: cyanosis typora-copy-images-to: imgsRedisson 分布式锁&#xff1f;在项目中哪里使用&#xff1f;多久会进行释放&#xff1f;如何加强一个分布式锁&#xff1f; 答&#xff1a; 首先入门级别的分布式锁是通过 setnx 进行实现&#xff0c;使用 setnx 实现有四个注意…

6.MySQL内置函数

个人主页&#xff1a;Lei宝啊 愿所有美好如期而遇 日期函数 current_date() 当前日期 select 可以做表达式和函数的计算。 current_time() 当前时间 current_timestamp() 当前日期加时间 注意&#xff1a;值得说明的是这三个函数底层调用的都是同一个函数&#xff0c;只不…

C语言关键字

关键字作用 关键字是 C 语言中预先保留的单词 C语言关键字 注意&#xff1a;在定义变量或者常量的时候&#xff0c;不要使用关键字

设计模式:组合模式(C#、JAVA、JavaScript、C++、Python、Go、PHP)

简介&#xff1a; 组合模式&#xff0c;它是一种用于处理树形结构、表示“部分-整体”层次结构的设计模式。它允许你将对象组合成树形结构&#xff0c;以表示部分和整体的关系。这种模式的主要目的是简化客户端代码&#xff0c;并使客户端以一致的方式处理单个对象和组合对象。…

数字图像处理实验记录五(图像的空间域增强-锐化处理)

前言&#xff1a; 文章目录 一、基础知识1&#xff0c;什么是锐化&#xff1f;2&#xff0c;为什么要锐化&#xff1f;3&#xff0c;怎么进行锐化&#xff1f; 二、实验要求任务1&#xff1a;任务2&#xff1a;任务3&#xff1a; 三、实验记录&#xff1a;任务1&#xff1a;任…

计算机网络第三章习题

1.假定1km长的CSMA/CD网络的数据率为1Gb/s。设信号在网络上的传播 速率为200000km/s。求能够使用此协议的最短帧长. 问题刨析: 逻辑链: 最短帧长数据传输率x争用期2τ(2倍端到端所需要的时间) 题目已经给出数据率为1Gb/s,所以我们要知道争用期2τ是多少. 端到端所需要的时间信…

订单 延后自动关闭,五种方案优雅搞定!

前 言 在开发中&#xff0c;往往会遇到一些关于延时任务的需求。例如 生成订单30分钟未支付&#xff0c;则自动取消生成订单60秒后,给用户发短信 对上述的任务&#xff0c;我们给一个专业的名字来形容&#xff0c;那就是延时任务 。那么这里就会产生一个问题&#xff0c;这个…

画程序流程图

一。在线程序流程图。类图和时序图 Integrations | Mermaid 二。VSCODE画UML图和各种种 1.下载plantuml.jarReleases plantuml/plantuml GitHubGenerate diagrams from textual description. Contribute to plantuml/plantuml development by creating an account on GitHu…

B-tree(PostgreSQL 14 Internals翻译版)

概览 B树(作为B树访问方法实现)是一种数据结构&#xff0c;它使您能够通过从树的根向下查找树的叶节点中所需的元素。为了明确地标识搜索路径&#xff0c;必须对所有树元素进行排序。B树是为有序数据类型设计的&#xff0c;这些数据类型的值可以进行比较和排序。 下面的机场代…

【在英伟达nvidia的jetson-orin-nx上使用调试摄像头-初步调试USB摄像头与Camera Conn.#0/#1接口-基础测试】

【在英伟达nvidia的jetson-orin-nx上使用调试摄像头-初步调试USB摄像头与Camera Conn.#0/#1接口-基础测试】 1、概述2、实验环境3、 物品说明&#xff08;0&#xff09;各种各样摄像头&#xff08;1&#xff09;USB摄像头&#xff08;2&#xff09;CSI摄像头&#xff08;3&…

【JavaEE】线程安全的集合类 -- 多线程篇(9)

线程安全的集合类 多线程环境使用 ArrayList多线程环境使用队列多线程环境使用哈希表 多线程环境使用 ArrayList 自己使用同步机制 (synchronized 或者 ReentrantLock)Collections.synchronizedList(new ArrayList); synchronizedList 是标准库提供的一个基于 synchronized 进…

【AIGC】百度文库文档助手之 - 一键生成PPT

百度文库文档助手之 - 一键生成PPT 引言一、文档助手&#xff1a;体验一键生成PPT二、文档助手&#xff1a;进阶用法三、其它生成PPT的方法3.1 ChatGPT3.2 文心一言 引言 就在上个月百度文库升级为一站式智能文档平台&#xff0c;开放四大AI能力&#xff1a;智能PPT、智能总结、…

正则表达式提取http和http内容

http.* 这样匹配到的就是我们要的内容 取反正则&#xff1a;^((?!要取反的正则表达式).)*$ 取反&#xff1a;^((?!http.).)$ 这样匹配到的就是我们不要的内容 提取域名 /[(http|ftp|https):\/\/]?([\w_-](?:(?:\.[\w_-])))([\w.,?^%&:\/~#-]*[\w?^%&\/~#-]…

APK与小程序渗透

文章目录 APK与小程序渗透1. APK2. 小程序2.1 源代码2.2 小程序的默认下载位置 3. 安装证书3.1 openssl配置环境变量3.2 安装证书 APK与小程序渗透 由于APK和小程序与服务器通信还是采用的是https协议&#xff0c;只是使用了加密。只要获取到了HTTP的请求报文就可以回归到Web渗…

单目3D目标检测论文汇总

基于语义和几何约束的方法 1. Deep3DBox 3D Bounding Box Estimation Using Deep Learning and Geometry [CVPR2017] https://arxiv.org/pdf/1612.00496.pdfhttps://zhuanlan.zhihu.com/p/414275118 核心思想&#xff1a;通过利用2D bounding box与3D bounding box之间的几何约…

『C语言进阶』字符函数和内存函数(2)

&#x1f525;博客主页&#xff1a; 小羊失眠啦. &#x1f516;系列专栏&#xff1a; C语言、Linux、Cpolar ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 文章目录 一、strtok函数1.1 函数认识1.2 注意事项 二、strerror函数2.1 函数认识2.2 注意事项 三、memcpy函数3.1 函数…

1024渗透测试如何暴力破解其他人主机的密码(第十一课)

1024渗透测试如何暴力破解其他人主机的密码(第十一课) 1 crunch 工具 crunch是一个密码生成器&#xff0c;一般用于在渗透测试中生成随机密码或者字典攻击。下面是常见的一些使用方法&#xff1a; 生成密码字典 生成6位数字的字典&#xff1a;crunch 6 6 -t -o dict.txt …

【LeetCode】145. 二叉树的后序遍历 [ 左子树 右子树 根结点]

题目链接 文章目录 Python3方法一&#xff1a; 递归 ⟮ O ( n ) ⟯ \lgroup O(n) \rgroup ⟮O(n)⟯方法二&#xff1a; 迭代 ⟮ O ( n ) ⟯ \lgroup O(n) \rgroup ⟮O(n)⟯方法三&#xff1a; Morris ⟮ O ( n ) 、 O ( 1 ) ⟯ \lgroup O(n)、O(1) \rgroup ⟮O(n)、O(1)⟯写…

学成在线第二天-查询课程、查询课程分类、新增课程接口实现以及跨域的处理思路和全局异常处理的使用以及面试题

目录 一、接口的实现 二、跨域的处理思路 三、全局异常处理 四、面试题 五、总结 一、接口的实现 1. 查询课程接口 思路&#xff1a; 典型的分页查询 按需查询 模糊查询的查询 controller&#xff1a; ApiOperation(value "课程列表", notes "课程…

字节码进阶之java Instrumentation原理详解

文章目录 0. 前言1. 基础2. Java Instrumentation API使用示例 3. Java Agent4. 字节码操作库5. 实际应用6. 注意事项和最佳实践 0. 前言 Java Instrumentation是Java API的一部分&#xff0c;它允许开发人员在运行时修改类的字节码。使用此功能&#xff0c;可以实现许多高级操…