string的使用和模拟实现
💓博主个人主页:不是笨小孩👀
⏩专栏分类:数据结构与算法👀 C++👀 刷题专栏👀 C语言👀
🚚代码仓库:笨小孩的代码库👀
⏩社区:不是笨小孩👀
🌹欢迎大家三连关注,一起学习,一起进步!!💓

string
- string的介绍
- 结构
- string的常用接口
- 构造和析构
- 遍历string
- size和length
- capacity
- resize和reserve
- 运算符重载
- push_back和append
- insert和erase
- 查找
- 截取字符串
- 获取字符指针
- 交换对象
- 特殊读取
string的介绍
string是一个类,可以看成是一个用模板写的顺序表,它的底层结构和顺序表基本是一样的,一个字符指针和一个表示存储数据的个数的size,还有一个表示容量大小的capacity,我们知道C++需要兼容C语言,所以它的字符串后面也是需要有一个‘\0’,的但是size和capacity的大小是不包含这个‘\0’的,因此实际的容量要比capacity大一个。
- 字符串是表示字符序列的类。
- 标准的字符串类提供了对此类对象的支持,其接口类似于标准字符容器的接口,但添加了专门用于操作单字节字符字符串的设计特性。
- string类是使用char(即作为它的字符类型,使用它的默认char_traits和分配器类型。
- string类是basic_string模板类的一个实例,它使用char来实例化basic_string模板类,并用char_traits和allocator作为basic_string的默认参数。
- 注意,这个类独立于所使用的编码来处理字节:如果用来处理多字节或变长字符(如UTF-8)的序列,这个类的所有成员(如长度或大小)以及它的迭代器,将仍然按照字节(而不是实际编码的字符)来操作。
总结:
- string是表示字符串的字符串类。
- 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。
- string在底层实际是:basic_string模板类的别名,typedef basic_string<char, char_traits, allocator> string;
- 不能操作多字节或者变长字符的序列。
结构
string的结构和顺序表很相似,一个存放字符的指针,还有一个标记个数的size,还有一个标记容量的capacity。
class
{private:char* _str;size_t _size;size_t _capacity;
}
string的常用接口
构造和析构
构造函数我们可以用个缺省参数,直接搞定无参和有参的情况,然后使用初始化列表把size和capacity给初始化一下,然后开个空间把参数拷贝过去就可以了。
析构函数的话很简单,直接释放空间即可,把其他值置一下0即可。
//构造
string(const char* s = ""): _size(strlen(s)), _capacity(_size)
{_str = new char[_capacity + 1];strcpy(_str, s);
}
//析构
~string()
{delete[] _str;_str = nullptr;_size = _capacity = 0;
}
遍历string
- 我们可以使用for循环来遍历

因为库里的string支持了[]运算符重载,我们可以就像遍历数组那样来遍历string。
int main()
{string s("123456");for (int i = 0; i < 6; i++){cout << s[i] << " ";}cout << endl;return 0;
}
因为它是传引用返回的,所以我们可读可写。它重载了一个const版本,所以当你传const对象时,就只能读不能写。
int main()
{string s("123456");for (int i = 0; i < 6; i++){s[i]++;}cout << s << endl;return 0;
}
- 使用迭代器遍历
迭代器我们可以理解为一个指针,然后需要一个范围,库里有几个函数,可以帮我们确定这个范围。

如果我们要正着遍历可以使用begin和end。


它的返回值是一个iterator的迭代器类型,然后这个类型在string类里面的,所以我们需要确定类域。
int main()
{string s("123456");string::iterator it = s.begin();while (it != s.end()){cout << *it << " ";it++;}cout << endl;return 0;
}
如果要倒着遍历,就需要使用了,rbegin和rend。


只不过他们的返回类型是reverse_iterator。
int main()
{string s("123456");string::reverse_iterator it = s.rbegin();while (it != s.rend()){cout << *it << " ";it++;}cout << endl;return 0;
}
这两对都是可读可写的,还剩两对前面带c的,是只能读,不能写。具体可戳标题详解。
- 使用范围for
int main()
{string s("123456");for (auto e : s){cout << e << " ";}cout << endl;return 0;
}
size和length
size和length的功能是一样的,都是返回字符串的长度,和C语言的strlen很相似。


int main()
{string s("123456");cout << s.size() << endl;cout << s.length() << endl;return 0;
}
capacity
capacity是返回已经开辟的容量的大小。

int main()
{string s("123456");cout << s.capacity() << endl;return 0;
}
模拟实现
const size_t size() const
{return _size;
}
const size_t capacity() const
{return _capacity;
}
resize和reserve
- resize
resize可以修改size的大小。

库里重载两个版本,resize分3个版本,它可能比capacity大,也可能在size和capacity中间,也有可能比size小。如果他比capacity大的话,会扩容,并且如果我们给字符参数,就会在我们原本的字符串后面全补我们给的那个字符,直到size变成我们设置的那个,我们没给字符的话,会补‘\0’,如果在size和capacity中间,不会扩容,但是还是会补充,如果比我们的size还小的话,就充当删除的功能了。
int main()
{string s("123456");s.resize(20);s.resize(20,'x');s.resize(10);s.resize(4);return 0;
}
- reserve
reserve可以设置capacity的大小,如果我们知道字符串多大的话,我们可以提前开好空间,因为让系统自己自动扩容的话,可能会频繁扩容,消耗太大了,所以我们在知道需要存储的字符串多大时可以提起开好空间。

int main()
{string s("123456");cout << s.capacity() << endl;s.reserve(100);cout << s.capacity() << endl;return 0;
}
模拟实现
void reserve(size_t n)
{if (n > _capacity){char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}}
void resize(size_t n, char c = '\0')
{if (n <= _size){_str[n] = '\0';_size = n;}else{reserve(n);size_t begin = _size;_size = n;while (begin < n){_str[begin] = c;begin++;}_size = n;_str[begin] = '\0';}
}
运算符重载
- 赋值运算符

也就是说我们可以直接用string对象或者字符串来给新的对象赋值。
int main()
{string s;s = "123456";string s1("xxx");s = s1;return 0;
}
- +和+=


从这两张图我们就可以看出来,+=是成员函数,而+是全局函数。我们需要尾插是可以用+=也可以用+,但是+的代价是很大的,我们尽量使用+=。
int main()
{string s("123");s += "456";s += '7';s = s + '8';return 0;
}
模拟实现
string& operator+=(const char s)
{push_back(s);return *this;
}string& operator+=(const char* s)
{append(s);return *this;
}
- 流插入和流提取


也就是说,我们可以直接使用cout和cin来对sting对象来进行输入和输出了。
模拟实现
- 流提取
我们可以一个字符一个字符的提取,因为_str为私有的,我们无法访问,我们也可以使用友元,但是我们这里一个字符一个字符的访问。
ostream& operator<<(ostream& out, const string& s)
{int end = s.size();for (int i = 0; i < end; i++){out << s[i];}return out;
}
- 流插入
流插入也一样,我们一个字符一个字符的读,当读到‘\n’或者空格就停止,但是cin和scanf都无法读空格和换行,这是就需要一个函数,cin.get(),它的功能就和C语言的getcahr很相似。

istream& operator>>(istream& in, string& s)
{s.clear();char ch = in.get();while (ch != ' ' && ch != '\n'){s += ch;ch = in.get();}return in;
}
int main()
{string s;cin >> s;cout << s << endl;return 0;
}
- 比较运算符

我们可以直接使用比较运算符来对sting对象和字符串等进行比较了。
模拟实现
char& operator[](size_t pos)
{assert(pos < _size);return _str[pos];
}const char& operator[](size_t pos) const
{assert(pos < _size);return _str[pos];
}string& operator+=(const char s)
{push_back(s);return *this;
}string& operator+=(const char* s)
{append(s);return *this;
}bool operator<(const string& s)
{return strcmp(_str, s._str) < 0;
}bool operator==(const string& s)
{return strcmp(_str, s._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);
}
push_back和append
- push_back

尾插一个字符
- append

尾插一段字符串,库里面给的接口很多,我们都不是很常用,我们都喜欢用+=。
模拟实现
void push_back(const char ch)
{if (_size == _capacity){reserve(_capacity == 0 ? 4 : _capacity * 2);}_str[_size++] = ch;_str[_size] = '\0';}
void append(const char* s)
{size_t len = strlen(s);if (_size + len > _capacity){reserve(_size+len);}strcpy(_str + _size, s);_size += len;
}
insert和erase
- insert

我们一般用的都是在某个位置插入一个字符或者字符串。
模拟实现
string& insert(size_t pos, char ch)
{assert(pos <= _size);if (_size == _capacity){reserve(_capacity == 0 ? 4 : _capacity * 2);}int end = _size;//必须强转,不然会类型提升,当pos=0时发生死循环while (end >= (int)pos){_str[end + 1] = _str[end];end--;}_str[pos] = ch;_size++;return *this;
}string& insert(size_t pos, const char* str)
{size_t len = strlen(str);if (_size + len > _capacity){reserve(_size + len);}int end = _size;while (end >= (int)pos){_str[end + len] = _str[end];end--;}strncpy(_str + pos, str, len);_size += len;return *this;
}
- erase

删除某个位置的len个字符
模拟实现
earse我们需要提供一个npos的半缺省参数,如果不给的话就表示将pos以后得数据都删了。
nops是一个所有类都有的,所以我们可以定义为静态成员变量。
string& erase(size_t pos, size_t len = npos)
{assert(pos < _size);if (pos + len > _size || len == npos){_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;}
}
public:const static int npos = -1;
查找
- find

我们可以用这个函数来查找字符串或者字符。
int main()
{string s("123456");int i = s.find("23");int j = s.find('5',2);int x = s.find("123", 2, 3);return 0;
}
- rfind

rfind也是查找只不过是从最后开始找。和find相似。
模拟实现
size_t find(char c, size_t pos = 0) const{assert(pos < _size);for (size_t i = pos; i < _size; i++){if (_str[i] == c){return i;}}return npos;}size_t find(const char* s, size_t pos = 0) const{size_t len = strlen(s);assert(pos + len < _size);char* str = strstr(_str + pos, s);if (str != nullptr){return str - _str;}return npos;}
截取字符串
substr

从pos位置开始提取len个字符,不给len直接取到最后。
模拟实现
string substr(size_t pos = 0, size_t len = npos) const
{string s;if (len == npos || pos + len > _size){size_t i = 0;for (i = pos; i <= _size; i++){s += _str[i];}return s;}else{size_t i = 0;for (i = pos; i < pos+len; i++){s += _str[i];}s += '\0';return s;}
}
获取字符指针
我们知道string是一个类,那我们想要得到底层的那个字符指针怎么获得呢?
我们可以使用c_str这个函数

int main()
{string s("123456");const char* str = s.c_str();return 0;
}
模拟实现
const char* c_str() const
{return _str;
}
交换对象
swap

对于string有一个专门的swap,可以用来交换两个string对象。
int main()
{string s("123456");string s1("12");s.swap(s1);cout << s << endl << s1 << endl;return 0;
}
模拟实现
void swap(string& s){std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);}
特殊读取
我们知道cin和scanf一样,遇到空格或者换行都会提前结束,那我们需要读取的字符串包含空格或者换行怎么办呢,这时候就需要getline了。

int main()
{string s;getline(cin, s);cout << s;return 0;
}
我们也可以自己指定读取结束的字符。
今天的分享就到这里,感谢大家的关注和支持。
相关文章:
string的使用和模拟实现
💓博主个人主页:不是笨小孩👀 ⏩专栏分类:数据结构与算法👀 C👀 刷题专栏👀 C语言👀 🚚代码仓库:笨小孩的代码库👀 ⏩社区:不是笨小孩👀 🌹欢迎大…...
基础算法---区间合并
直接上题目,不废话! 题目 给定 n 个区间 [l,r],要求合并所有有交集的区间。 注意如果在端点处相交,也算有交集。 输出合并完成后的区间个数。 例如:[1,3] 和 [2,6] 可以合并为一个区间 [1,6]。 输入格式 第一行包含整数 n。 接下来 n 行&am…...
C++(day4)
思维导图 封装Mystring #include <iostream> #include<cstring>using namespace std;class Mystring{ public://无参构造函数Mystring():size(10){strnew char[size];strcpy(str,"");cout<<"无参构造函数"<<endl;}//有参构造函数…...
docker 部署 node.js(express) 服务
1、在 express 项目根目录下新增 Dockerfile 文件,内容如下: 创建服务容器的方法,可以根据自己的情况选择: 1、以下示例为宿主机没有安装 node 环境的写法; 2、先在本地构建包含 node 和 express 的基础镜像࿰…...
商城系统开发,如何确保用户数据的安全性?
确保用户数据的安全性是商城系统开发中至关重要的一项任务。随着数字化时代的到来,用户的个人信息和交易数据已成为黑客和不法分子的重要目标,因此保护用户数据的安全性对于商城系统的成功运营至关重要。在开发商城系统时,以下几个方面是确保…...
黑客必备工具Kali Linux,安装与使用教程全包含,从入门到精通,全网最详细全面的Kali Linux教程
Kali Linux是一个高级渗透测试和安全审计Linux发行版,目前可以说是网络安全人员的专用系统。 Kali Linux功能非常强大,能够进行信息取证、渗透测试、攻击WPA / WPA2保护的无线网络、离线破解哈希密码、将android、Java、C编写的程序反编译成代码等等&am…...
2024滴滴校招面试真题汇总及其讲解(二)
4.【基础题】HashMap了解吗?介绍一下它对应的线程安全版本。 HashMap 是 Java 中一种键值对映射的集合,它使用哈希表来存储键值对。HashMap 具有插入和删除元素效率高的优势,但不是线程安全的。 ConcurrentHashMap 是 Java 中一种线程安全的 HashMap,它使用分段锁来保证线…...
嵌入式-C语言中的if语句
目录 一.if语句介绍 二.案例实操 2.1C语言运行模板代码 2.2运行方法 2.3案例 一.if语句介绍 if判断语句是一种用于根据条件来进行条件分支的控制流语句。通过判断一个条件的真假来决定执行不同的代码块。if语句的基本语法如下:if (条件表达式) {// 如果条件为…...
组合数 rust解法
组合数。 编写函数,参数是两个非负整数n和m,返回组合数 C n m C_n^m Cnm,其中m≤n≤25。 例如,n25,m12时答案为5200300。 解法: fn c(n: u32, m: u32)->u64 {let m if m > n-m {n-m}else{m};le…...
【SpringMVC】自定义注解与AOP结合使用
目录 一、SpringMVC之自定义注解 1.1 Java注解简介 1.2 为什么要用注解 1.3 注解的分类 ⭐ 1.3.1 JDK基本注解 1.3.2 JDK元注解 1.3.3 自定义注解 1.4 自定义注解三种使用案例 1.4.1 案例一(获取类与方法上的注解值) 1.4.2 案例二࿰…...
MyEclipse 用tomcat部署SSM项目后,项目名称和当前项目不一致
MyEclipse 用tomcat部署SSM项目后,项目成功启动,但是访问所有接口报404 从这里可以看到,部署的项目名为accurate_sugar_control_yc_api,但实际我们项目名字应该为accurate_sugar_control_otc_api 解决办法 在本地找到项目的根目…...
来喽!!炒鸡详细的“数据在内存中的存储”真的来喽!
目录 1. 整数在内存中的存储 1.1 ⼆进制介绍 1.1.1 2进制转10进制 1.1.2 10进制转2进制 1.1.3 2进制转8进制 1.1.4 2进制转16进制 1.2 原码、反码、补码 2. ⼤⼩端字节序和字节序判断 2.1 什么是⼤⼩端? 2.2 为什么有⼤⼩端? 2.3 练习 …...
【面试经典150 | 双指针】验证回文串
文章目录 写在前面Tag题目来源题目解读解题思路方法一:筛选判断方法二:原地判断 知识回顾回文串双指针字符串操作 写在最后 写在前面 本专栏专注于分析与讲解【面试经典150】算法,两到三天更新一篇文章,欢迎催更…… 专栏内容以分…...
sql存储引擎
-- 查询建表语句 --可以查看引擎 show create table account; -- 可以看到默认引擎 InnoDB ENGINEInnoDB -- 查看当前数据库支持得存储引擎 show engines ; # InnoDB 默认 存储引擎 # MyISAM sql早期默认 存储引擎 # MEMORY 存储在内存中 用来做临时表和缓存 存储引擎 …...
Visual Studio 2022安装SVN插件教程
1. 第一步:避免踩坑,超级重要!!!关闭Visual Studio 2022应用程序;(不然插件装不上,一直转圈!) 2.第二步:下载Visual Studio 2022版本对应的SVN插件…...
【PyCharm Community Edition】:串口开发
串口开发 安装模块:pyserial端口检查:uartDevice自定义文件:SerialMonitor.py导入自定义文件:SerialMonitor.py延伸阅读 安装模块:pyserial Pyserial 是 Python 中使用串口通信的一个第三方库,使用它可以方…...
亲测可用!!!Centos7安装chrome+chromedriver以便实现selenium自动化详细教程
网上很多教程都是在线安装chrome,这样安装了最新稳定的chrome,可惜我遇到chromdriver的版本跟上 chrome,为了早日实现在centos服务selenium自动化,不可能去等待 chromdriver 更新,只能 chrome进行降版本来离线安装。花…...
spring cloud、gradle、父子项目、微服务框架搭建---cloud gateway(十)
总目录 https://preparedata.blog.csdn.net/article/details/120062997 文章目录 总目录一、简介二、order、pay服务 配置context-path三、新建gateway网关服务(1) 启动类添加 SpringCloudApplication 即可(2) application.yml 配…...
AD22使用笔记+积累库
一、前言 使用AD9习惯了,但是需求逐渐上来了就不够用了,好多快捷的新功能要新版本软件才能用,所以升级使用AD22 目录 1.添加层之后中间层无法布线 2.新增快捷方式CtrlW布线,不用点图标了 二、环境 AD22 三、正文 1.添加层之…...
20230912在ubuntu18.04下使用pigz来提高tar命令压缩解压缩的速度
20230912在ubuntu18.04下使用pigz来提高tar命令压缩解压缩的速度 2023/9/15 22:19 https://blog.csdn.net/wb4916/article/details/128447298 20221226编译Toybrick的TB-RK3588X开发板的Android12系统2-SDK预处理 4、单线程压缩。 建议使用:pigz多线程压缩…...
测试微信模版消息推送
进入“开发接口管理”--“公众平台测试账号”,无需申请公众账号、可在测试账号中体验并测试微信公众平台所有高级接口。 获取access_token: 自定义模版消息: 关注测试号:扫二维码关注测试号。 发送模版消息: import requests da…...
synchronized 学习
学习源: https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖,也要考虑性能问题(场景) 2.常见面试问题: sync出…...
人工智能(大型语言模型 LLMs)对不同学科的影响以及由此产生的新学习方式
今天是关于AI如何在教学中增强学生的学习体验,我把重要信息标红了。人文学科的价值被低估了 ⬇️ 转型与必要性 人工智能正在深刻地改变教育,这并非炒作,而是已经发生的巨大变革。教育机构和教育者不能忽视它,试图简单地禁止学生使…...
Golang——7、包与接口详解
包与接口详解 1、Golang包详解1.1、Golang中包的定义和介绍1.2、Golang包管理工具go mod1.3、Golang中自定义包1.4、Golang中使用第三包1.5、init函数 2、接口详解2.1、接口的定义2.2、空接口2.3、类型断言2.4、结构体值接收者和指针接收者实现接口的区别2.5、一个结构体实现多…...
Kubernetes 网络模型深度解析:Pod IP 与 Service 的负载均衡机制,Service到底是什么?
Pod IP 的本质与特性 Pod IP 的定位 纯端点地址:Pod IP 是分配给 Pod 网络命名空间的真实 IP 地址(如 10.244.1.2)无特殊名称:在 Kubernetes 中,它通常被称为 “Pod IP” 或 “容器 IP”生命周期:与 Pod …...
Leetcode33( 搜索旋转排序数组)
题目表述 整数数组 nums 按升序排列,数组中的值 互不相同 。 在传递给函数之前,nums 在预先未知的某个下标 k(0 < k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k1], …, nums[n-1], nums[0], nu…...
pycharm 设置环境出错
pycharm 设置环境出错 pycharm 新建项目,设置虚拟环境,出错 pycharm 出错 Cannot open Local Failed to start [powershell.exe, -NoExit, -ExecutionPolicy, Bypass, -File, C:\Program Files\JetBrains\PyCharm 2024.1.3\plugins\terminal\shell-int…...
华为OD最新机试真题-数组组成的最小数字-OD统一考试(B卷)
题目描述 给定一个整型数组,请从该数组中选择3个元素 组成最小数字并输出 (如果数组长度小于3,则选择数组中所有元素来组成最小数字)。 输入描述 行用半角逗号分割的字符串记录的整型数组,0<数组长度<= 100,0<整数的取值范围<= 10000。 输出描述 由3个元素组成…...
深入浅出JavaScript中的ArrayBuffer:二进制数据的“瑞士军刀”
深入浅出JavaScript中的ArrayBuffer:二进制数据的“瑞士军刀” 在JavaScript中,我们经常需要处理文本、数组、对象等数据类型。但当我们需要处理文件上传、图像处理、网络通信等场景时,单纯依赖字符串或数组就显得力不从心了。这时ÿ…...
篇章一 论坛系统——前置知识
目录 1.软件开发 1.1 软件的生命周期 1.2 面向对象 1.3 CS、BS架构 1.CS架构编辑 2.BS架构 1.4 软件需求 1.需求分类 2.需求获取 1.5 需求分析 1. 工作内容 1.6 面向对象分析 1.OOA的任务 2.统一建模语言UML 3. 用例模型 3.1 用例图的元素 3.2 建立用例模型 …...
