【C++】string类 (模拟实现详解 下)

       

         我们接着上一篇【C++】string类 (模拟实现详解 上)-CSDN博客继续对string模拟实现。从这篇内容开始,string相关函数的实现就要声明和定义分离了。

1.reserve、push_back和append

string.hstring类里进行函数的声明。

void reserve(size_t n); //扩容
void push_back(const char x);//尾插字符
void append(const char* str);//尾插字符串

string.cpp中进行函数的实现。这个文件要包含include "string.h",同样用命名空间。

n小于_capacity的情况都默认为空间不变,n大于_capacity扩容。

#include "string.h"
namespace lyj //命名空间
{void string::reserve(size_t n){if (n > _capacity){char* temp = new char[n + 1];//多开一个,留给'\0'strcpy(temp, _str);//把_str里的数据拷贝到新空间delete[] _str; //释放旧空间_str = temp; //让_str指向新空间_capacity = n; //更新_capacity的值}}
}

reserve实现好了,push_back可以直接复用reserve的扩容。还是在命名空间里实现。

void string::push_back(const char x)//尾插字符
{if (_size == _capacity)//两者相等时空间不够{//扩容,2倍扩,如果_capacity为0,就直接给4string::reserve(_capacity == 0 ? 4 : _capacity * 2);}_str[_size] = x;//插入字符++_size;//更新_size的值 _str[_size] = '\0';//末尾一定是'\0'
}

新插入的x会把原来末尾的\0覆盖,所以我们要在新末尾加上\0。

append也是需要复用reserve。push_back是2倍扩容,append我们就要分情况决定扩容大小了。

void string::append(const char* str)//尾插字符串
{size_t len = strlen(str);//计算要插入的字符串的长度if (_size + len > _capacity)//如果空间不够,扩容{//如果插入的字符串特别长,2倍扩就会扩容频繁,所以在这里判断一下reserve(_size + len > _capacity * 2 ? _size + len : _capacity * 2);}strcpy(_str + _size, str);//从原字符串的'\0'出开始拷贝新的str_size += len; //更新_size的大小
}

如果插入的字符串特别长,2倍扩就会扩容频繁,所以如果原字符串长度加上插入的新字符串长度比_capacity的两倍要,我们就不以2倍扩容,直接扩到原字符串长度加上插入的新字符串长度那么大,反之,还是以2倍扩。

test.cpp中测试一下。

void test2()
{string s1("hello world");s1.push_back('x');s1.append("yyyyyyyyyyyyyyyyyyy");cout << s1.c_str() << endl;
}
int main()
{lyj::test2(); //指定命名空间调用函数return 0;
}

2.operator+=

operator+=有两种形式,+=字符+=字符串

string.hstring类里进行函数的声明。

string& operator+=(char ch);//+=字符
string& operator+=(const char* str);//+=字符串

string.cpp中进行函数的实现。

 前面push_back实现好了之后,operator+=字符就可以直接复用他。

string& string::operator+=(char ch)
{push_back(ch);return *this;
}

同样的,append实现好之后,operator+=字符串就可以直接复用他。

string& string::operator+=(const char* str)//+=字符串
{append(str);return *this;
}

 在test.cpp中测试一下。

void test3()
{string s1("hello world");s1 += '$';s1 += '\n';s1 += "hello csdn";cout << s1.c_str() << endl;
}
int main()
{lyj::test3(); //指定命名空间调用函数return 0;
}

3.insert

insert我们也实现两个形式,插入字符和插入字符串。 

string.hstring类里进行函数的声明。

void insert(size_t pos, char ch);//插入字符
void insert(size_t pos, const char* str);//插入字符串

3.1 插入字符

string.cpp中进行函数的实现。

假设我们现在要在2位置插入x

执行_str[_size+1]==_str[_size] 。

然后--_size。 

然后再执行_str[_size+1]==_str[_size] 。

在while循环里重复执行,当_size等于pos时。

 

所以循环退出的条件是_size<pos。然后把_size+1的字符换成插入的x。

 我们再考虑特殊情况,当_size为0,此时_size+1为1。上面的代码可行。但是我们不可以直接用_size遍历,这样就直接改变_size的值,我们用size来遍历。代码如下。

void string::insert(size_t pos, char ch)//插入字符
{assert(pos >= 0 && pos <= _size);int size = _size; //用size遍历,不用_sizeif (_size = _capacity)//空间不够开空间{string::reserve(_capacity == 0 ? 4 : _capacity * 2);}while (size >= pos){_str[size + 1] = _str[size];--size;}_str[size + 1] = ch;++_size;
}

但是我们会发现,当size=0时,代码运行崩溃了。因为C语言里,两个数比较的时候会自动发生类型提升。所以这里的int类型的size被提升成了和pos一样的类型,导致代码死循环。所以这里要做一个类型转换。

void string::insert(size_t pos, char ch)//插入字符
{assert(pos >= 0 && pos <= _size);int size = _size; //用size遍历,不用_sizeif (_size = _capacity)//空间不够开空间{string::reserve(_capacity == 0 ? 4 : _capacity * 2);}while (size >= (int)pos) //类型转换{_str[size + 1] = _str[size];--size;}_str[size + 1] = ch;++_size;
}

 在test.cpp中测试一下。

void test4()
{string s;string s1("hello world");s1.insert(0, 'x');cout << s1.c_str() << endl;
}
int main()
{lyj::test4(); //指定命名空间调用函数return 0;
}

3.2 插入字符串

string.cpp中进行函数的实现。

假设我们现在要在3位置插入8个x。

我们要在位置3往后留出len个长度。

 让_str[size+len]=_str[size]。

然后size--; size+len也就前移了。

再执行 _str[size+len]=_str[size];

一直循环,到size等于pos。

 执行 _str[size+len]=_str[size];

然后size--;

此时数据的移动就完成了。我们把8个x插进去。_str+pos位置开始插,插len个。

void string::insert(size_t pos, const char* str)//插入字符串
{assert(pos >= 0 && pos <= _size);size_t len = strlen(str);//提前计算str的长度int size = _size; //用size遍历,不用_sizeif (_size + len > _capacity)//空间不够开空间{//如果str太长,不按2倍扩reserve(_size + len > _capacity * 2 ? _size + len : _capacity * 2);}while (size >= (int)pos)//要类型转换{_str[size + len] = _str[size];--size;}//把str插进去,起始位置是_str+pos//这里不用strcpy,因为strcpy会把\0也拷贝过去。memcpy(_str + pos, str, len);//用memcpy_size += len;//更新_size的值
}

这里不用strcpy,因为strcpy会把\0也拷贝过去,我们选择用memcpy,拷贝len个字节,就是str的内容大小。

 在test.cpp中测试一下。

void test4()
{string s;string s1("hello world");s1.insert(3, "xxxxxxxx");cout << s1.c_str() << endl;
}

头插、尾插、中间插入都是没问题的 。

4.erase

把pos位置开始的len个字符删了。删除数据的时候,参数列表第二个参数要给一个缺省值npos,这个npos要自己定义,在string类里。

private:char* _str;size_t _size;size_t _capacity;static const size_t npos = -1;//这里可以直接初始化,只有整形可以

string.hstring类里进行函数的声明。

void erase(size_t pos, size_t len = npos);//删除

string.cpp中进行函数的实现。

分两种情况讨论。

(1)len比pos后面的剩余的字符数大。

 

所以我们只需要把pos位置改为\0。此时_size=pos. 

(2) len比pos后面的剩余的字符数小。

 

 

这样就可以了,第一个\0后面的东西根本不用管。此时_size大小为_size-len。 

void string::erase(size_t pos, size_t len)//删除
{assert(_size > 0);//删除数据前提是有数据assert(pos >= 0 && pos < _size);if (len >= _size - pos)//情况1{_str[pos] = '\0';_size = pos;}else //情况2{while (pos + len <= _size){_str[pos] = _str[pos + len];pos++;}_size -= len;}
}

test.cpp中测试一下。

void test5()
{string s;string s1("hello world");s1.erase(0, 1);//头删cout << s1.c_str() << endl;s1.erase(5, 3);//中间删cout << s1.c_str() << endl;s1.erase(3);//不传第二个参数cout << s1.c_str() << endl;
}

结果没问题。

string类的模拟实现就说这么多。模拟实现目的是帮助我们更好的理解string。

本篇就到这里,拜拜~

 

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

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

相关文章

JVM(HotSpot):GC之垃圾回收器的分类

文章目录 前言一、串行二、吞吐量优先三、响应时间优先四、常见垃圾回收器使用组合 前言 上一篇&#xff0c;我们学习了分代回收机制 它的主要内容是对JVM内存的一个划分&#xff0c;以及垃圾回收器工作时&#xff0c;区域运作顺序的一个规定。 所以&#xff0c;它是一个规范。…

Spring Boot论坛网站:开发、部署与管理

3系统分析 3.1可行性分析 通过对本论坛网站实行的目的初步调查和分析&#xff0c;提出可行性方案并对其一一进行论证。我们在这里主要从技术可行性、经济可行性、操作可行性等方面进行分析。 3.1.1技术可行性 本论坛网站采用SSM框架&#xff0c;JAVA作为开发语言&#xff0c;是…

智慧楼宇平台,构筑未来智慧城市的基石

随着城市化进程的加速&#xff0c;城市面临着前所未有的挑战。人口密度的增加、资源的紧张、环境的恶化以及对高效能源管理的需求&#xff0c;都在推动着我们寻找更加智能、可持续的城市解决方案。智慧楼宇作为智慧城市建设的重要组成部分&#xff0c;正逐渐成为推动城市可持续…

MATLAB电化学特性评估石墨和锂电

&#x1f3af;要点 模拟对比石墨电池的放电电压曲线与实验数据定性差异。对比双箔、多相多孔电极理论和锂电有限体积模型实现。通过孔隙电极理论模型了解粗粒平均质量和电荷传输以及孔隙率的表征意义。锂电中锂离子正向和逆向反应速率与驱动力的指数以及电解质和电极表面的锂浓…

Docker 部署 EMQX 一分钟极速部署

部署 EMQX ( Docker ) [Step 1] : 拉取 EMQX 镜像 docker pull emqx/emqx:latest[Step 2] : 创建目录 ➡️ 创建容器 ➡️ 拷贝文件 ➡️ 授权文件 ➡️ 删除容器 # 创建目录 mkdir -p /data/emqx/{etc,data,log}# 创建容器 docker run -d --name emqx -p 1883:1883 -p 1808…

「Qt Widget中文示例指南」如何实现半透明背景?

Qt 是目前最先进、最完整的跨平台C开发工具。它不仅完全实现了一次编写&#xff0c;所有平台无差别运行&#xff0c;更提供了几乎所有开发过程中需要用到的工具。如今&#xff0c;Qt已被运用于超过70个行业、数千家企业&#xff0c;支持数百万设备及应用。 本文将为大家展示如…

《Linux从小白到高手》综合应用篇:深入理解Linux常用关键内核参数及其调优

1. 题记 有关Linux关键内核参数的调整&#xff0c;我前面的调优文章其实就有涉及到&#xff0c;只是比较零散&#xff0c;本篇集中深入介绍Linux常用关键内核参数及其调优&#xff0c;Linux调优80%以上都涉及到内核的这些参数的调整。 2. 文件系统相关参数 fs.file-max 参数…

Excel 对数据进行脱敏

身份证号脱敏&#xff1a;LEFT(A2,6)&REPT("*",6)&RIGHT(A2,6) 手机号脱敏&#xff1a;LEFT(B2,3)&REPT("*",5)&RIGHT(B2,3) 姓名脱敏&#xff1a;LEFT(C2,1)&REPT("*",1)&RIGHT(C2,1) 参考&#xff1a; excel匹配替换…

STM32F103C8T6 IO 操作

1.开启相关时钟 在 STM32 微控制器中&#xff0c;开启 GPIO 端口的时钟是确保 IO 口可以正常工作的第一步。 查找 RCC 寄存器使能时钟 在 STM32 中&#xff0c;时钟控制的寄存器通常位于 RCC (Reset and Clock Control) 模块中。不同的 STM32 系列&#xff08;如 STM32F1、STM…

【单元测试】深入解剖单元测试的思维逻辑

目录 一、前言二、准备环境三、 常用的mock语句3.1 模拟指定类的对象实例&#xff0c;用于模拟依赖对象&#xff08;类成员&#xff09;3.2 定义被测试对象3.3 模拟枚举类型/静态方法3.4 模拟依赖方法3.5 模拟构造方法3.6 验证方法调用次数3.7 验证返回值3.8 验证异常对象 四、…

第十六周:机器学习笔记

第十六周周报 摘要Abstratc一、机器学习1. Pointer Network&#xff08;指针网络&#xff09;2. 生成式对抗网络&#xff08;Generative Adversarial Networks | GAN&#xff09;——&#xff08;上&#xff09;2.1 Generator&#xff08;生成器&#xff09;2.2 Discriminator&…

ssm企业库存管理微信小程序-计算机毕业设计源码82704

摘 要 本文基于SSM框架&#xff0c;设计与实现了一个企业库存管理微信小程序。该小程序主要包括用户登录、库存查询、入库操作、出库操作等功能模块。在设计过程中&#xff0c;采用了前后端分离的架构&#xff0c;前端使用了微信小程序原生开发工具进行开发&#xff0c;后端使用…

【C++篇】探索STL之美:熟悉使用String类

CSDN 文章目录 前言 &#x1f4ac; 欢迎讨论&#xff1a;如果你在学习过程中有任何问题或想法&#xff0c;欢迎在评论区留言&#xff0c;我们一起交流学习。你的支持是我继续创作的动力&#xff01; &#x1f44d; 点赞、收藏与分享&#xff1a;觉得这篇文章对你有帮助吗&…

ApacheShiro反序列化 550 721漏洞

Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理个漏洞被称为 Shiro550 是因为在Apache Shiro的GitHub问题跟踪器中&#xff0c;该漏洞最初被标记为第550个问题,721漏洞名称也是由此而来 Shiro-550 CVE-2016-4437 Shiro反序列化Docker复现 …

Android GPU Inspector分析帧数据快速入门

使用 谷歌官方工具Android GPU Inspector (AGI) 可以对Android 应用进行深入和全面的系统性能分析和帧性能分析 。AGI 是一个非常强大的分析工具&#xff0c;尤其是在需要诊断 GPU 性能问题和优化应用时&#xff0c;可以帮助你精准找到性能瓶颈。本文介绍如何使用该工具对帧数据…

HTTP Proxy环境下部署Microsoft Entra Connect和Health Agents

在企业环境中&#xff0c;时常需要通过使用HTTP Proxy访问Internet&#xff0c;在使用HTTP Proxy访问Internet的环境中部署Microsoft Entra Connect和Microsoft Entra Connect Health Agents可能会遇到一些额外的配置步骤&#xff0c;以便这些服务能够正常连接到Internet。 一…

Windows系统PyCharm右键运行.sh文件

在参考了Windows系统下pycharm运行.sh文件&#xff0c;执行shell命令_shell在pycharm-CSDN博客 和深度学习&#xff1a;PyCharm中运行Bash脚本_pycharm bash-CSDN博客 配置了右键执行.sh文件之后&#xff0c;发现在Windows的PyCharm中直接右键运行sh文件&#xff0c;存在如下…

【MyBatis】MyBatis-config标签详解

目录 MyBatis配置文件标签详解configuration标签properties标签typeAliases标签environments标签environment标签transactionManager标签dataSource标签mappers标签 MyBatis配置文件标签详解 我们在使用MyBatis框架的时候需要一个配置文件——MyBatis-config.xml来告诉MyBatis…

Android按钮Button

Button是程序用于和用户进行交互的一个重要控件。Button也是继承自TextView&#xff0c;既可以显示文本&#xff0c;又可以显示图片&#xff0c;二者在UI上的区别主要是 Button 控件有个按钮外观&#xff0c;提示用户单击。 图1 Button示意图 Button最主要的功能是通过单击来执…

K折交叉验证代码实现——详细注释版

正常方法 #---------------------------------Torch Modules -------------------------------------------------------- from __future__ import print_function import numpy as np import pandas as pd import torch.nn as nn import math import torch.nn.functional as …