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大一个。

  1. 字符串是表示字符序列的类。
  2. 标准的字符串类提供了对此类对象的支持,其接口类似于标准字符容器的接口,但添加了专门用于操作单字节字符字符串的设计特性。
  3. string类是使用char(即作为它的字符类型,使用它的默认char_traits和分配器类型。
  4. string类是basic_string模板类的一个实例,它使用char来实例化basic_string模板类,并用char_traits和allocator作为basic_string的默认参数。
  5. 注意,这个类独立于所使用的编码来处理字节:如果用来处理多字节或变长字符(如UTF-8)的序列,这个类的所有成员(如长度或大小)以及它的迭代器,将仍然按照字节(而不是实际编码的字符)来操作。

总结:

  1. string是表示字符串的字符串类。
  2. 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。
  3. string在底层实际是:basic_string模板类的别名,typedef basic_string<char, char_traits, allocator> string;
  4. 不能操作多字节或者变长字符的序列。

结构

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

  1. 我们可以使用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;
}
  1. 使用迭代器遍历

迭代器我们可以理解为一个指针,然后需要一个范围,库里有几个函数,可以帮我们确定这个范围。

在这里插入图片描述
如果我们要正着遍历可以使用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的,是只能读,不能写。具体可戳标题详解。

  1. 使用范围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

  1. 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;
}
  1. 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';}
}

运算符重载

  1. 赋值运算符

在这里插入图片描述

也就是说我们可以直接用string对象或者字符串来给新的对象赋值。

int main()
{string s;s = "123456";string s1("xxx");s = s1;return 0;
}
  1. +和+=

在这里插入图片描述

在这里插入图片描述

从这两张图我们就可以看出来,+=是成员函数,而+是全局函数。我们需要尾插是可以用+=也可以用+,但是+的代价是很大的,我们尽量使用+=。

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;
}
  1. 流插入和流提取
    在这里插入图片描述
    在这里插入图片描述

也就是说,我们可以直接使用cout和cin来对sting对象来进行输入和输出了。

模拟实现

  1. 流提取
    我们可以一个字符一个字符的提取,因为_str为私有的,我们无法访问,我们也可以使用友元,但是我们这里一个字符一个字符的访问。
ostream& operator<<(ostream& out, const string& s)
{int end = s.size();for (int i = 0; i < end; i++){out << s[i];}return out;
}
  1. 流插入

流插入也一样,我们一个字符一个字符的读,当读到‘\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;
}
  1. 比较运算符
    在这里插入图片描述
    我们可以直接使用比较运算符来对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

  1. push_back
    在这里插入图片描述

尾插一个字符

  1. 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

  1. 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;
}
  1. 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;

查找

  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;
}
  1. 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;
}

我们也可以自己指定读取结束的字符。

今天的分享就到这里,感谢大家的关注和支持。

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

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

相关文章

iisfastchi漏洞复现

1.查看版本 2.在路径中写入 php脚本 发现使用不了 3.环境搭建 结局打开 把限制打开

Kafka中Consumer源码解读

Consumer源码解读 本课程的核心技术点如下&#xff1a; 1、consumer初始化 2、如何选举Consumer Leader 3、Consumer Leader是如何制定分区方案 4、Consumer如何拉取数据 5、Consumer的自动偏移量提交 Consumer初始化 从KafkaConsumer的构造方法出发&#xff0c;我们跟踪到…

前端JavaScript中异步的终极解决方案:async/await

&#x1f3ac; 岸边的风&#xff1a;个人主页 &#x1f525; 个人专栏 :《 VUE 》 《 javaScript 》 ⛺️ 生活的理想&#xff0c;就是为了理想的生活 ! 1. 背景 在深入讨论 async/await 之前&#xff0c;我们需要了解一下 JavaScript 的单线程和非阻塞的特性。JavaScript 是…

AD22使用笔记+积累库

一、前言 使用AD9习惯了&#xff0c;但是需求逐渐上来了就不够用了&#xff0c;好多快捷的新功能要新版本软件才能用&#xff0c;所以升级使用AD22 目录 1.添加层之后中间层无法布线 2.新增快捷方式CtrlW布线&#xff0c;不用点图标了 二、环境 AD22 三、正文 1.添加层之…

Redis Part1

单体架构&#xff1a;一台Web服务器、一台数据库服务器。 1.了解NoSql 什么是Nosql&#xff1f; NoSQL&#xff0c;即Not-Only-SQL&#xff0c;意思就是我们干事情不能只用SQL&#xff0c;泛指非关系型的数据库&#xff01;NoSQL定位&#xff1a;作为关系型数据库的补充&am…

小米OPPO三星一加红魔全机型解锁BL详细教程合集-ROOT刷机必要操作

解锁BL一个熟悉又陌生的词汇&#xff0c;只要你刷机root过&#xff0c;你肯定都解锁BL成功过。我们简单的描述下BL是什么&#xff1f;BL全名bootloader&#xff0c;目前市面上全部机型&#xff0c;基本出厂全部BL处于锁定的状态锁定的BL机型&#xff0c;不支持刷入非官方固件或…

性能测试 —— Jmeter事务控制器

事务&#xff1a; 性能测试中&#xff0c;事务指的是从端到端&#xff0c;一个完整的操作过程&#xff0c;比如一次登录、一次 筛选条件查询&#xff0c;一次支付等&#xff1b;技术上讲&#xff1a;事务就是由1个或多个请求组成的 事务控制器 事务控制器类似简单控制器&…

广州口腔医院种植牙-广东省爱牙工程公益种牙,获湾区群众点赞

广州种植牙价格表-自2017年成立以来,广东省爱牙工程一直坚持以公益惠民为宗旨、公益种牙为服务方向,针对群众普遍存在的口腔健康问题,开展形式多样的公益性口腔医疗惠民活动。 广州种植牙费用表-日前,广东省爱牙工程“种植牙惠民行动”第二十季已正式启动。据广东省爱牙工程官方…

基于ntchat的微信群聊同步机器人

微信群有500人上限的限制&#xff0c;建立多个群的话又有信息无法互通的不便&#xff0c;此机器人通过自动将消息转发到同一个同步组内的所有群&#xff0c;消除这一不便性&#xff0c;间接达成扩大群成员数的目的。 效果演示&#xff1a; 项目地址&#xff1a; https://gith…

为何红黑树在B/B+树之上仍然占据重要地位?

为何红黑树在B/B树之上仍然占据重要地位&#xff1f; 引言二、红黑树和B/B树的基本原理2.1、红黑树的特点和性质2.2、B/B树的特点和性质2.3、红黑树和B/B树的比较 三、B/B树相对于红黑树的优势四、红黑树仍然占据重要地位的原因总结 博主简介 &#x1f4a1;一个热爱分享高性能服…

Vue echarts 饼图 引导线加小圆点,文字分行展示

需求 重点代码 完整代码 initChart() {// 创建 echarts 实例。var myChartOne this.$echarts.init(this.$refs.Echart);myChartOne.setOption({tooltip: {trigger: "item",},title: {top: center,text: [{name| this.chartTitle.name },{value| this.chartTitle.…

ROS学习笔记(四)---使用 VScode 启动launch文件运行多个节点

ROS学习笔记文章目录 01. ROS学习笔记(一)—Linux安装VScode 02. ROS学习笔记(二)—使用 VScode 开发 ROS 的Python程序&#xff08;简例&#xff09; 03. ROS学习笔记(三)—好用的终端Terminator 一、什么是launch文件 虽然说Terminator终端是能够比较方便直观的看运行的节点…

2.10 PE结构:重建重定位表结构

Relocation&#xff08;重定位&#xff09;是一种将程序中的一些地址修正为运行时可用的实际地址的机制。在程序编译过程中&#xff0c;由于程序中使用了各种全局变量和函数&#xff0c;这些变量和函数的地址还没有确定&#xff0c;因此它们的地址只能暂时使用一个相对地址。当…

虚幻动画系统概述

本文主要整理一下高层次的概述&#xff0c;方便后续查阅 1.动画流程 DCC产出动画文件 -> UE动画导入 -> 动画蓝图驱动&#xff08;类似unity的动画状态机&#xff09; ->动画后处理蓝图驱动&#xff08;例如修型骨&#xff0c;骨骼矫正等后期处理&#xff09; 2.动…

数据结构与算法:树

目录 树 定义 结构 二叉树 定义 结构 形式 满二叉树 完全二叉树 存储 链式存储结构 数组 孩子节点 父节点 应用 查找 维持相对顺序 遍历 深度优先遍历 前序遍历 中序遍历 后序遍历 广度优先遍历 层序遍历 二叉堆 定义 自我调整 操作 插入加点 删…

左神算法之中级提升班(9)

目录 【案例1】 【题目描述】 【思路解析】 【代码实现】 【案例2】 【题目描述】 【思路解析 平凡解技巧 从业务中分析终止条件 重点】 【代码实现】 【案例3】 【题目描述】 【思路解析】 【案例4】 【题目描述】 【思路解析】 【代码实现】 【动态规划代码】…

使用SSH地址拉取远程仓库代码报下面的错误

说明&#xff1a;配置了SSH秘钥后&#xff0c;使用SSH地址克隆代码&#xff0c;依旧无法拉取代码&#xff0c;提示下面这个信息。 Their offer&#xff1a;ssh-rsa&#xff0c;ssh-dss fatal&#xff1a;Could not read from remote repository. Please make sure you have the…

sqlserver2012性能优化配置:设置性能相关的服务器参数

前言 sqlserver2012 长时间运行的话会将服务器的内存占满 解决办法 通过界面设置 下图中设置最大服务器内存 通过执行脚本设置 需要先开发开启高级选项配置才能设置成功 设置完成之后将高级选择配置关闭&#xff0c;还原成跟之前一样 --可以配置高级选项 EXEC sp_conf…

开源库源码分析:Okhttp源码分析(一)

开源库源码分析&#xff1a;OkHttp源码分析 导言 接下来就要开始分析一些常用开源库的源码了&#xff0c;作为最常用的网络请求库&#xff0c;OkHttp以其强大的功能深受Android开发者的喜爱&#xff08;比如说我&#xff09;&#xff0c;还有对该库进行二次封装而成的热门库&a…