【C++】string类--常见接口及其模拟实现

目录

1. 遍历

1.1. 下标+operator[ ]

1.2. c_str

1.3. 迭代器

1.4. 范围for

2. 增

2.1. push_back

2.2. 重载+=(char ch)

2.3. appand

2.4. 重载+=(char* ch)

2.5. insert(任意位置插入)

2.5.1. 任意位置插入一个字符

2.5.2. 任意位置插入一个字符串

3. 删

3.1. earse

情况一:len==npos说明要全部删除完

情况二:并不全部删除完

4. 查

4.1. 查找一个字符

4.2. 查找一个字符串

4.3. 字符串比较

5. 改

5.1. reserve

5.2. resize

情况一:resize的n大于当前字符串空间长度(注意是空间长度)

情况二:resize的n小于_capacity,但是大于_size

情况三:resize的n小于当前字符串长度


上一篇文章我们讲解了string类的构造、拷贝构造、赋值及其模拟实现
下面是我们本篇文章的主要内容:

1. 遍历

1.1. 下标+operator[ ]

char& operator[](size_t pos)
{assert(pos < strlen(_str));return _str[pos];
}const char& operator[](size_t pos) const 
{assert(pos < strlen(_str));return _str[pos];
}int main()
{string s1("hello world");for (int i = 0; i < s1.size(); ++i){cout << s1[i];}return 0;
}


1.2. c_str

也就是说c_str返回的值指向该字符串并且包含“\0”的字符序列,使用c_str( )来打印字符串,当碰到“\0”时就停止(这是因为C/C++中字符串处理函数(如printfcoutstrcpy等)都遵循一个约定:将以null字符('\0')作为字符串的结束标志)。

const char* c_str() const
{return _str;
}


1.3. 迭代器

迭代器:string的迭代器的底层其实就是一个char*的原生指针,所以使用string迭代器只需要像使用普通指针一样即可。但是其他容器的底层不应该否是原生指针。

typedef char* iterator;
typedef const char* const_iterator;
iterator begin()
{return _str;
}iterator begin() const
{return _str;
}
iterator end()
{return _str + _size;
}iterator end() const
{return _str + _size;
}

然后我们使用迭代器来进行字符串的遍历:

int main()
{s::string s1("Hello World");s::string::iterator it = s1.begin();while (it != s1.end()){cout << *it << "";it++;}
}

PS:这个地方要注意的是要注意使用的是C++string类中的迭代器还是我们自定义类string中的迭代器。


1.4. 范围for

for的使用:

for (auto e : s1)
{cout << e << " ";
}

其实范围for的底层机制同样是一个迭代器,我们可以通过下面的方式进行验证,我们将迭代器给注释掉,我们来看一下发生什么:


2. 增

2.1. push_back

可以看到,push_back的作用是将一个字符添加到原有字符串后面,具体步骤:

  • 我们先要判断时候需要扩容:如果size==capacity,说明满了。扩容:开辟一个两倍内存的新空间,然后原有字符串拷贝至新空间,释放原空间
  • 添加的新字符应该放在size的位置
  • 然后处理“\0”即可

2.2. 重载+=(char ch)

但是我们在日常写代码中并不会经常使用push_back,而是使用 += ,所以我们来重载一下 += ,它的实现中也可以服用push_back:

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

2.3. appand

这里可以注意到,前面两个接口都是将单个字符添加到已有字符串结尾,而这个接口是将一个字符串添加到原来的字符串,步骤如下:

  • 首先需要考虑要不要扩容,而且这里的扩容不能只是简单地将空间变为两倍,因为这样并不能保证空间足够。应该是将原字符串长度和新添加字符串长度相加,这样得到的空间就一定能满足要求,这里我们使用reserve函数(见下面的5.1)
  • 然后更新 _size
		void append(const char* str){size_t len = _size + strlen(str);if (len > _capacity){reserve(len);}strcpy(_str + _size, str);_size = len;//insert(_size, str);}

2.4. 重载+=(char* ch)

与单个字符的重载复用push_back一样,复用appand接口:

	string& operator+=(const char* str){append(str);return *this;}

2.5. insert(任意位置插入)

2.5.1. 任意位置插入一个字符

  • 首先判断pos位置是否合法
  • 然后判断是否需要扩容
  • 然后从结尾的"\0"开始,从后往前一次向后移动一位,到pos为止
  • 插入新字符,然后更新_size

string& insert(size_t pos, char ch)
{assert(pos <= _size);if (_capacity == _size){reserve(_capacity == 0 ? 4 : _capacity * 2);}size_t end = _size + 1;while (end > pos){_str[end - 1] = _str[end];--end;}_str[pos] = ch;++_size;return *this;
}

2.5.2. 任意位置插入一个字符串

  • 插入位置是否合法
  • 判断是否需要扩容,这里需要扩大到原字符串长度+插入字符串长度
  • 将原字符串包括pos在内的后面所有字符(包括"\0")往后移动len=strlen(ch)位
  • 然后将新字符串插入,然后更新_size

		string& insert(size_t pos, const char* str){assert(pos <= _size);size_t len = strlen(str);if (len == 0){return *this;}if (_size + len > _capacity){reserve(_size + len);}//挪动数据size_t end = _size + len;//while(end >= end+len)while (end > pos+len-1){_str[end] = _str[end-len];--end;}//插入数据  size_t i = 0;while (i < len){_str[pos+i] = str[i];++i;}_size += len;return *this;}

3. 删

3.1. earse

我们看这里提到了一个参数npos:

所以可以知道,earse的作用就是从pos开始,往后len个长度,把这些字符删去。

情况一:len==npos说明要全部删除完

那其实我们逻辑上删除它只需要将pos位置赋值"\0"即可:

情况二:并不全部删除完

首先找到需要删除的子字符串的后一位,将其定为begin,然后我们需要做的就是将begin后面的所有字符全部往前移动,覆盖掉需要删除的字符:

string& earse(size_t pos, size_t len = std::string::npos)
{assert(pos < _size);if (len == std::string::npos || len + pos >= _size){_str[pos] = '\0';_size = pos;return *this;}else{size_t begin = pos + len;while (begin <= _size){_str[begin - len] = _str[begin];++begin;}_size -= len;return *this;}
}

4. 查

4.1. 查找一个字符

find查找字符串,返回查到的第一个满足的字符:

size_t find(char ch, size_t pos = 0)
{for (; pos < _size; ++pos){if (_str[pos] == ch){return pos;}}return std::string::npos;
}

4.2. 查找一个字符串

size_t find(const char* ch, size_t pos)
{const char* p = strstr(_str + pos, ch);if (nullptr == p){return std::string::npos;}else{return p - _str;}
}

4.3. 字符串比较

bool operator < (const string& s)
{return strcmp(this->c_str(), s.c_str()) < 0;
}
bool operator == (const s::string& s)
{return strcmp(this->c_str(), s.c_str()) == 0;
}
bool operator<=(const string& s)
{return *this < s || *this == s;
}
bool operator>(const string& s)
{return !(*this <= s);
}
bool operator>=(const string& s)
{return !(*this < s);
}
bool operator!=(const string& s)
{return !(*this == s);
}

对于这段代码有疑问的可以看下【C++】类和对象--类中6个默认成员函数(2) --运算符重载,这里涉及到成员函数隐藏this指针的问题。


5. 改

5.1. reserve

reserve的作用就是是个string的容量变成n:

  • 如果n小于等于_capacity,则不进行扩容
  • 否则开辟一个容量为n的char*,然后进行拷贝,注意释放掉原有的内存
void reserve(size_t n)
{if (n > _capacity){char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}
}

5.2. resize

resize是将字符串大小修改为n。

resize和reserve的区别是:reserve只对空间进行处理,但是resize不仅对空间进行影响,而且会改变_size的值。

情况一:resize的n大于当前字符串空间长度(注意是空间长度)

看下面这个例子,假设空间长度为15,字符串长度为11:

也就是扩容了之后会使用字符串参数填充满:

情况二:resize的n小于_capacity,但是大于_size

这种情况下不需要扩容,所以_capacity不会变:

情况三:resize的n小于当前字符串长度

这个时候只会保存原字符串的前n个字符:

代码如下:

//扩空间+初始化
//删除部分数据,保留前n个
void resize(size_t n, char ch = '\0')
{if (n < _size){_size = n;_str[_size] = '\0';}else{if (n > _capacity){reserve(n);}for (size_t i = _size; i < n; ++i){_str[i] = ch;}_size = n;_str[_size] = '\0';}}

(本篇完)

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

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

相关文章

SCADA 云化部署核心:WebSocket 协议实现毫秒级远程控制

在浙江某智慧水厂的中控室里&#xff0c;曾发生过一次惊险的远程控制失误&#xff1a;运维人员通过传统 SCADA 系统&#xff08;工业控制系统的 “大脑”&#xff09;远程调节水泵转速&#xff0c;指令发出后&#xff0c;屏幕上却迟迟没有反馈 —— 等水泵转速最终变化时&#…

大数据电商流量分析项目实战:Day1-2 补充 软件安装和Zookeeper

✨博客主页&#xff1a; https://blog.csdn.net/m0_63815035?typeblog &#x1f497;《博客内容》&#xff1a;大数据、Java、测试开发、Python、Android、Go、Node、Android前端小程序等相关领域知识 &#x1f4e2;博客专栏&#xff1a; https://blog.csdn.net/m0_63815035/…

EMC电磁兼容进阶3讲培训:专题三 近场探头和频谱仪在EMC整改中的应用

一节课&#xff0c;名企实战型工程师让你了解近场探头与频谱分析仪在EMC整改中的应用&#xff0c;从实际整改测试出发&#xff0c;结合实际项目案例进行讲解。一顿聚餐的费用&#xff0c;助您入门一个很有前景的行业&#xff01; 注&#xff1a;不是卖资料&#xff01;不是卖资…

使用动态IP 需要注意什么

网络安全防护动态IP会频繁变更&#xff0c;需确保防火墙和杀毒软件实时更新&#xff0c;防止因IP变动导致的安全漏洞。避免在公共网络环境下登录敏感账户&#xff0c;建议使用VPN加密连接。服务稳定性管理某些在线服务&#xff08;如远程办公、游戏服务器&#xff09;可能因IP变…

GitHub自动化利器:Probot框架实战指南

引言 在当今快节奏的软件开发世界中&#xff0c;自动化已成为提高生产力和保证代码质量的关键要素。GitHub作为全球最大的代码托管平台&#xff0c;其丰富的API生态系统为自动化提供了无限可能。Probot作为一个基于Node.js的开源框架&#xff0c;专门用于构建GitHub应用程序&a…

第十四届蓝桥杯青少组C++选拔赛[2023.2.12]第二部分编程题(4、最大空白区)

参考程序1&#xff1a;#include <bits/stdc.h> using namespace std;int main() {int N, M;cin >> N >> M;vector<vector<int>> grid(N, vector<int>(M));for (int i 0; i < N; i)for (int j 0; j < M; j)cin >> grid[i][j]…

文心一言-Agent岗三轮面试全记录

面经分享&#xff5c;文心一言-Agent岗三轮面试全记录 前段时间面试了 文心一言团队 - 大模型 Agent 岗&#xff0c;三轮面试下来感触颇多。整体来说&#xff0c;文心团队的面试节奏偏“循序渐进”&#xff1a;一面看基础&#xff0c;二面看综合素养&#xff0c;三面看思考深度…

【大前端++】几大特征

大纲 大前端业务模型结构如下&#xff1a; 服务后台大前端原生系统可定制的终端硬件 1、业务的起点技术结构基于跨平台前端框架 Electronvue/Rect/其他web框架js/ts FlutterDartvue/Rect/其他web框架js/ts 其他前端框架结构 2、有特定的业务使用场景 人脸识别考勤 数字…

计算机网络---网络体系结构

文章目录1. 网络的概念1.1 什么是计算机网络1.2 简单的计算机网络1.3 互联网&#xff08;或因特网&#xff0c;Internet&#xff09;1.4 计算机网络、互连网和互联网三者的区别1.5 总结2. 网络的组成、功能2.1 组成2.1.1 从组成部分看2.1.2 从工作方式看2.1.3 从逻辑功能看2.2 …

机器学习超参数调优全方法介绍指南

本篇文章Master Hyperparameter Tuning in Machine Learning适合希望深入了解超参数调优的读者。文章的亮点在于介绍了多种调优方法&#xff0c;如手动搜索、网格搜索、随机搜索、贝叶斯优化和元启发式算法&#xff0c;并通过实际案例展示了这些方法在复杂模型&#xff08;如CN…

怎么降低 AIGC 生成率?

怎么降低 AIGC 生成率&#xff1f;市面上那些号称 "AI 降重神器" 的工具真的有用吗&#xff1f;想和大家聊聊我的看法 ——人工修改生成内容&#xff0c;可能是目前最靠谱的办法。一、AI 降重工具的实际效果现在很多工具说能通过 AI 降低 AIGC 生成率&#xff0c;原理…

心磁图 QRS 参数在 Brugada 综合征心律失常风险分层中的应用

研究背景Brugada 综合征是一种与致命性室性心律失常及心源性猝死风险相关的遗传性心脏离子通道病&#xff0c;其典型特征为右胸导联&#xff08;V1-V3&#xff09;出现特征性ST段抬高&#xff08;1型、2型或3型 Brugada 心电图表现&#xff09;。然而&#xff0c;静息心电图呈现…

Futuring robot旗下家庭机器人F1将于2025年面世

2025年9月10日&#xff0c;张翼二次创业的机器人公司Futuring Robot发布了第一款家庭服务机器人F1。这款F1机器人不仅具备端茶送水、物品递送、家庭整理等日常服务能力&#xff0c;还深度融合了多项教育辅助功能&#xff0c;如学习陪伴、棋类对弈、作业进度管理等&#xff0c;旨…

User类CRUD实现

代码&#xff1a; WYend/Myblog_springbook3: 我的第一个个人网站&#xff08;后端版&#xff09; 随时更新 一、数据库的构建 交给ai 二、各类注解 Lombok注解 Data&#xff1a; 自动生成类的getter、setter、toString()、equals()、hashCode()方法适用于实体类&#xff…

【Linux | 网络】数据链路层

一、以太网1.1 认识以太网1.2 以太网帧格式1.3 MAC地址1.3.1 认识MAC地址1.3.2 MAC地址的类型1.3.3 MAC地址 VS IP地址1.4 局域网如何通信1.5 局域网数据碰撞1.5.1 数据碰撞1.5.2 划分碰撞域&#xff08;交换机&#xff09;二、ARP协议2.1 ARP协议的作用2.2 ARP数据报的格式2.3…

Google Ads广告验证全攻略:如何借助动态住宅IP精准投放?

在竞争激烈的数字广告领域&#xff0c;Google Ads扮演着至关重要的角色。然而&#xff0c;随着广告政策的不断更新和平台对广告质量要求的提高&#xff0c;广告验证已成为许多广告主绕不开的环节。同时&#xff0c;如何实现精准投放&#xff0c;将广告触达最相关的目标受众&…

鸿蒙Next Web组件生命周期详解:从加载到销毁的全流程掌控

想要精通鸿蒙应用开发&#xff1f;Web组件的9大生命周期回调是你必须掌握的上帝视角&#xff01;在鸿蒙应用开发中&#xff0c;Web组件是我们加载本地或在线网页的强大工具。它提供了完整的生命周期回调体系&#xff0c;让开发者能够精准感知网页加载的每个阶段&#xff0c;从而…

python学习进阶之异常和文件操作(三)

文章目录1.程序异常2.文件操作3.json操作1.程序异常 1.1 异常 异常概念&#xff1a; 程序在运行时, 如果Python解释器遇到到一个错误, 则会停止程序的执行, 并且提示一些错误信息, 这就是异常 抛出异常&#xff1a; 程序停止执行并且提示错误信息这个动作, 通常称之为抛出(ra…

NodeJS 8 ,从 0 到 1:npm 包发布与更新全流程指南( 含多场景适配与踩坑总结 )

目录 前言 一、准备工作 1.1 开发环境搭建 1.1.1 环境安装 1.1.2 配置问题 1.2 账号注册 1.2.1 账号注册&#xff08;两种方式&#xff09; 1.2.2 登录验证 1.2.3 个人设置 1.2.4 安全配置 1.3 初始配置 1.3.1 初始项目目录 1.3.2 关键字段详解 1.3.3 手动完善 二…

BERT中文预训练模型介绍

bert-base-chinese 是由谷歌基于 BERT&#xff08;Bidirectional Encoder Representations from Transformers&#xff09;模型预训练得到的适用于中文任务的模型版本。以下从多个方面对其进行详细解释&#xff1a; 模型概述 BERT 是一种基于 Transformer 架构的预训练语言模型…