【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;
}

结果没问题。

5.find

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

size_t find(char ch, size_t pos = 0);
size_t find(const char* str, size_t pos = 0);

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

size_t string::find(char ch, size_t pos)
{assert(pos < _size);size_t n = pos;while (_str[n] != ch){n++;if (n == _size){return npos;}}return n;
}
size_t string::find(const char* str, size_t pos = 0)
{assert(pos < _size);const char* ptr = strstr(_str + pos, str);if (ptr == nullptr){return npos;}else{return ptr - _str;//返回下标}
}

6.substr

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

string substr(size_t pos = 0, size_t len = npos);

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

string string::substr(size_t pos, size_t len)
{assert(pos < _size);if (len > _size - pos){len = _size - pos;}string sub;sub.reserve(len);for (size_t i = 0; i < len; i++){sub += _str[pos + i];}return sub;
}

 在test.cpp中测试一下。和find一起测试。

void test6()
{string s("test.cpp.zip");size_t pos = s.find('.');string suffix = s.substr(pos);cout << suffix.c_str() << endl;
}

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

本篇就到这里,拜拜~

 

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

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

相关文章

Maven项目报错:invalid LOC header (bad signature)

文章目录 Maven项目报错&#xff1a;invalid LOC header (bad signature)1. Maven项目加载或Pom.Xml刷新后仍出现如下错误2. 解决方法 Maven项目报错&#xff1a;invalid LOC header (bad signature) 1. Maven项目加载或Pom.Xml刷新后仍出现如下错误 错误提示&#xff1a; in…

【Spring MVC】请求参数的获取

我的主页 1. 获取 URL 中的参数 获取 URL 中的参数是通过 PathVariable 注解实现的 RequestMapping("/article/{articleId}") public String method12(PathVariable("articleId") String articleId){return "接收到参数articleId: " articleI…

书生第四期作业:L0G4000 任务作业

模型下载 在github-codespace 登录codespace安装依赖&#xff1a; 运行下载的py文件&#xff0c;下载模型配置文件&#xff0c; 下载完成 在intern-studio开发机 下载配置文件 下载完成

2024系统架构师---真题考试知识点

1.逻辑地址的构成是“逻辑地址&页内地址”&#xff0c;而物理地址的构成“物理块号&页内地址”&#xff0c;因此只要找出逻辑地址中那几位表示逻辑页号基本就完成了。页内大小4k4*2的10次方2的12次方&#xff0c;可以得出页内地址占12位&#xff0c;因此1B1AH的后12位&…

群控系统服务端开发模式-系统架构图

一、开发服务(1.0版本)&#xff1a; 平台前端服务、平台api接口服务、国外api处理服务&#xff1b; 二、开发服务(2.0版本)&#xff1a; 国内客户端前端服务、国内客户端api接口服务、国外客户端前端服务、国外客户端api接口服务&#xff1b; 三、运行服务(1.0版本)&#xff…

JavaWeb合集22-Apache POI

二十二、Apache POI Apache POI是一个处理Miscrosoft Office各种文件格式的开源项目。简单来说就是&#xff0c;我们可以使用POI在Java 序中对Miscrosoft Office各种文件进行读写操作。一般情况下&#xff0c;POI都是用于操作Excel文件。 使用场景&#xff1a;银行网银系统导出…

【Android】Kotlin教程(2)

文章目录 1.空安全2.let安全调用3.非空断言操作符!!4.空合并操作符 ?:5.异常6.先决条件函数7.substring8.split函数9.replace10.字符串比较11.安全转换函数12.标准库函数1.apply2.run3.with4.also5.takeIf6.takeUnless 1.空安全 为了避免NullPointerException&#xff0c;Kot…

【2024|滑坡数据集论文解读3】CAS滑坡数据集:用于深度学习滑坡检测的大规模多传感器数据集

【2024|滑坡数据集论文解读3】CAS滑坡数据集&#xff1a;用于深度学习滑坡检测的大规模多传感器数据集 【2024|滑坡数据集论文解读3】CAS滑坡数据集&#xff1a;用于深度学习滑坡检测的大规模多传感器数据集 文章目录 【2024|滑坡数据集论文解读3】CAS滑坡数据集&#xff1a;用…

计算机网络(十二) —— 高级IO

#1024程序员节 | 征文# 目录 一&#xff0c;预备 1.1 重新理解IO 1.2 五种IO模型 1.3 非阻塞IO 二&#xff0c;select 2.1 关于select 2.2 select接口参数解释 2.3 timeval结构体和fd_set类型 2.4 socket就绪条件 2.5 select基本工作流程 2.6 简单select的服务器代…

c++编解码封装

多态版编解码 对服务器和客户端的结构体进行序列化然后对数据进行反序列化 案例分析 代码demo Codec.h #pragma once #include <iostream>class Codec { public:Codec();virtual std::string encodeMsg();//string是标准库的string类virtual void* decodeMsg();virtu…

西瓜书《机器学习》符号表KaTex表示

写这篇post的缘故是最近整理机器学习的相关公式&#xff0c;经常要用到KaTex, 但网络上搜索到的西瓜书符号表的表示有些并不准确或不严谨&#xff0c;本着严谨治学的态度&#xff0c;整理了一下符号表的KaTex表示&#xff0c;希望有所帮助,整理过程中参考了《南瓜书》和 KaTex官…

Flutter TextField和Button组件开发登录页面案例

In this section, we’ll go through building a basic login screen using the Button and TextField widgets. We’ll follow a step-bystep approach, allowing you to code along and understand each part of the process. Let’s get started! 在本节中&#xff0c;我们…

软件系统建设方案书(word参考模板)

1 引言 1.1 编写目的 1.2 项目概述 1.3 名词解释 2 项目背景 3 业务分析 3.1 业务需求 3.2 业务需求分析与解决思路 3.3 数据需求分析【可选】 4 项目建设总体规划【可选】 4.1 系统定位【可选】 4.2 系统建设规划 5 建设目标 5.1 总体目标 5.2 分阶段目标【可选】 5.2.1 业务目…

ctfshow(259->261)--反序列化漏洞--原生类与更多魔术方法

Web259 进入界面&#xff0c;回显如下&#xff1a; highlight_file(__FILE__);$vip unserialize($_GET[vip]); //vip can get flag one key $vip->getFlag();题干里还提示了网站有一个flag.php界面&#xff0c;源代码如下&#xff1a; $xff explode(,, $_SERVER[HTTP_X…

Docker容器操作

Docker容器操作 启动容器 docker run -it 镜像名(镜像id) bash当利用docker run来创建容器时&#xff0c;Docker在后台运行的标准操作包括&#xff1a; 检查本地是否存在指定的镜像&#xff0c;不存在就从公有仓库中下载利用镜像创建并启动一个容器分配一个文件系统&#xf…

C语言实现Go的defer功能

之前笔者写了一篇博文C实现Go的defer功能&#xff0c;介绍了如何在C语言中实现Go的defer功能&#xff0c;那在C语言中是否也可以实现这样的功能呢&#xff1f;本文就将介绍一下如何在C语言中实现Go的defer功能。 我们还是使用C实现Go的defer功能中的示例&#xff1a; void te…

医院信息化与智能化系统(9)

医院信息化与智能化系统(9) 这里只描述对应过程&#xff0c;和可能遇到的问题及解决办法以及对应的参考链接&#xff0c;并不会直接每一步详细配置 如果你想通过文字描述或代码画流程图&#xff0c;可以试试PlantUML&#xff0c;告诉GPT你的文件结构&#xff0c;让他给你对应的…

改进YOLOv8系列:引入低照度图像增强网络Retinexformer | 优化低光照目标检测那题

改进YOLOv8系列:引入低照度图像增强网络Retinexformer | 优化低光照目标检测那题 🚀论文研究概括🚀加入到网络中的理论研究🚀需要修改的代码1 🍀🍀Retinexformer 代码2🍀🍀tasks里引用🚀创建yaml文件🚀测试是否创建成功前言:这篇论文提出了一种用于低光图像…

STM32应用详解(10)I2C总线初始化

文章目录 前言一、I2C总线初始化二、程序源码与详解1.I2C初始化2.I2C端口初始化及设置IO端口工作模式3.函数I2C_Init4.函数I2C_Cmd5.使能APB1外设时钟6.I2C通信时序图 前言 介绍STM32的I2C总线初始化&#xff0c;给出了代码详解。《i2c.h》文件&#xff0c;由用户编写。定义了…

系统聚类比较——最短距离法、最长距离法、重心法和类平均法

系统聚类概述 系统聚类&#xff0c;又称分层聚类法&#xff0c;是一种用于分析数据的统计方法&#xff0c;在生物学、分类学、社会网络等领域有广泛应用。以下是对系统聚类的详细概述&#xff1a; 一、基本思想 系统聚类的基本思想是将每个样品&#xff08;或数据点&#xf…