【C++杂货铺】详解string

f9b4d418a2c44c5d892c11ad88c04306.png


目录

 🌈前言🌈

📁 为什么学习string

📁 认识string(了解)

📁 string的常用接口

 📂 构造函数

 📂 string类对象的容量操作

 📂 string类对象的访问以及遍历操作​编辑

 📂 string类对象的修改操作

📁 模拟实现string

📁 总结


 🌈前言🌈

        欢迎观看本期【C++杂货铺】,本期内容将全面string,包含了解string,如何操作string,最后会模拟实现string

        因为计算机行业的不断发展,许多程序员不仅仅要掌握string这些容器的使用方法,有的公司会要求阅读底层源码,模拟实现这些容器,所以本期内容将会从零开始,带大家了解使用string。

        当然,本篇内容的参考文献主要来源于网站:<string> - C++ Reference (cplusplus.com)

        如果,你只想掌握string的具体使用方法,可以阅读下面这篇文章:

【C++杂货铺】详解string的接口-CSDN博客

b9fef61f29b6488b95f0417ce2f9f96a.gif

📁 为什么学习string

        在C语言中,字符串是以‘\0’结尾的一些字符的结合,为了操作方便,C标准库提供了一些str系列的库函数,但这些库函数与字符串是分开的,底层空间需要用户自己管理,可能造成越界访问等。

        在日常生活中,为了简单,方便,快捷,基本都会使用string类,很少会有人去使用C库中的字符串操作函数。

📁 认识string(了解)

        string是表示字符串的字符串。该类的接口与常规容器的接口基本相同,在添加了一系列专门用来操作string的常规操作。

        string是basic_string模板类的一个别名,用char来实例化basic_string模板类。

        string不能操作多字节或者边长字符的序列。

        在使用string时,必须包括#include头文件以及using namespace std;

📁 string的常用接口

        string接口有上百种,这里我们只介绍常用的,以及需要了解的。

 📂 构造函数

94c2d4027e764186a34f52cd9ceb244e.png

//函数名称                                      功能说明
string()   重点                       构造空的string类对象,即空字符串
string(const string* s)  重点         用C_string来构造string类对象
string(size_t n,char c)               string类对象中把包含了n个字符c
string(const string& s) 重点          拷贝构造
string s1;                //构造空的string类对象s1string s2("hello string");    //用C风格字符串构造string类对象s2string s3(s2);            //拷贝构造s3

 📂 string类对象的容量操作

46b6cf8bc8304be089e07124345df30b.png

1. size()

        size函数求的是字符串中元素的个数,不包含‘\0’。

//打印 5
string s("hello");
cout<<s.size()<<endl;

        其中length 和 size作用都是一样的,都是求有效字符串的字符长度,不包含 ‘\0’。size和length方法底层实现原理完全相同。引入size的原因是为了和其他容器接口保持一致。基本使用size。

2. reserve()

        reverse的作用就是为字符串预留空间,应用场景就是已知数据元素有多少,可以减少扩容的操作,减少消耗。

        但reserve()相当于手动扩容,但要注意的是,扩容量不能小于现有的容量。

    string  s;size_t cnt = s.capacity();cout << cnt << endl;cout<<"change:" << endl;for (int i = 0;i < 100;i++){s.push_back('a');if (cnt != s.capacity()){cout << s.capacity() << endl;cnt = s.capacity();}}

  8959243b79514a498db05f97e3a0144f.png

        以上是没有reserve的对象,第一次扩容两倍,之后是扩容1.5倍每次(vs是1.5倍扩容,Linux下按照2倍扩容)。

    string  s;s.reserve(100);size_t cnt = s.capacity();cout << cnt << endl;cout<<"change:" << endl;for (int i = 0;i < 100;i++){s.push_back('a');if (cnt != s.capacity()){cnt = s.capacity();cout << cnt << endl;}}

76ae8d032fa844fc8e7d51fa4370109b.png

        上图可知reseve的结果并不一定是准确的扩容数,可能会增加一些。

3. resize()

fd5cfaebfdbb48129b71f15ac22dcbd7.png

        resiz的功能是将有效字符的个数改成n;如果n大于有效字符个数,即n>size,则会插入;如果空间不够,即n>capacity,则会扩容+插入。当然传参没有char c则没有插入。

a0472b95a7344ece9b8f5cb577716288.png

 📂 string类对象的访问以及遍历操作53cbfa3de4c54b5f838ae2c2534c36ad.png

1. operator[ ]

8d0d5d06b8894d5498dfc3b08b75de88.png

        string类对象支持下标访问,[ ]支持读写pos位置的数据;const修饰为只读,不能修改。

//非const的对象使用[],可读可写
string s1("hello world");for(int i =0;i<s1.size();i++)
{s1[i] = 'a';
}for(int i=0;i<s1.size();i++)
{cout<<s1[i]<<' '<<endl;
}//const对象,只读
const string s2("hello world");
for(int i =0;i<s2.size();i++)
{cout<<s2[i]<<' '<<endl;
}

2. 迭代器 begin + end

        迭代器iterator,容器中类似与指针的东西,通过迭代器可以访问容器中的数据,使用方法也类似于指针。迭代器可能是指针,也可能不是。

        begin指向容器中第一个数据的位置;end指向容器中最后一个有效字符的下一个位置,即‘\0’( '\0'不算有效字符 )。

c501dc89adbf4b9eb59e346302bd0a54.png

31f9eae604264f03bd9f0167f9924950.png

        3601242138554ae385012930325f9ab3.png

string  s("hello world");for (string::iterator it = s.begin();it != s.end();it++)
{cout << *it << ' ';
}//打印 h e l l o  w o r l d

3. 迭代器 rbegin + rend

        rbegin就是reverse_begin的缩写,rbegin和rend主要用于逆序遍历。反向迭代器则是reverse_iterator. b27ad7dab610411ca76d3861e89667ac.png

379a62fdea0847dfa55a431c511bbb89.png

a6f25de948744222b636008eb1dfc728.png

string s("hello world");
for (string::reverse_iterator it = s.rbegin();it != s.rend();it++)
{cout << *it << ' ';
}

4. 范围for

        范围for的底层就是迭代器,将s的迭代器赋值给e(auto类型是编译器自动推导的数据类型)。

string  s("hello world");
for (auto e : s)
{cout << e << ' ';
}
cout << endl;

 📂 string类对象的修改操作

810008c5395c420c816d859acfee812b.png

1. push_back()

        尾部插入一个字符

//打印 hello w
string s("hello ");s.push_back('w');cout<<s<<endl;

2. append()2c9ea7a4c9e9406e9d73ac892bfc5197.png

        尾部插入一个字符串。

string s1("hello ");
s.append("world");
//打印 hello  worldstring s2("hello ");
s2.append(10, 'x');
//打印helloxxxxxxxxxxstring s3("hello ");
s3.append(s1.begin(),s1.end());
//打印hello hello world

3. operator+=

        尾部插入一个字符或者字符串。

5790d019892d4e2088de7605594ff040.png

string s1("hello ");
s1 += "world";string s2("aaaaa");
s2 += ' ';string s3("bbbbb");
s3 += s2;

4. insert()

        前面之前插入字符或者字符串。

c889e508d7984fbda77ca868f7f5195d.png

5. erase()

        删除从pos位置开始,len个字符,如果len没有传参赋初值,npos就代表着有多少删多少。

npos是string里面的一个静态成员变量static const size_t npos = -1; size_t是无符号整数,所以-1代表整数最大值。

51639864a48a4f56a5a2a45f5ab24c7b.png

        从pos位置开始,删除len个字符。或者从first迭代器开始,到last迭代器结束的字符删除。

string s("a bcd");
s.erase(0,1);
cout << s << endl;
//打印 bcd

6. replace()fbc3d5f790df44f889c01f4227581627.png

        将字符替换。

string s1("a bcd");
s1.replace(1,1,"a");
cout << s1 << endl;
//打印aabcstring s2("a bcd");
s2.replace(1,1,1,'a');
cout << s2 << endl;
//打印aabc

 insert,earse,replace等函数尽量少用,因为涉及数据元素的移动,效率太低。

5cc14a3430b541348fc2972e5e0bd43b.png

7. c_str()

        因为C++是要兼容C语言的,在C语言中,表示字符串使用char* 来表示的,所以C语言库中许多操作使用的是char* 来操作的。而在C++中,我们大多数使用的是string,那么如何将string类型转为C语言字符串类型呢?

        c_str()的作用就是从string中返回C格式字符串。

string filename("test.txt");
/*
FILE* file = fopen(filename,"r");
filename是string类型,而fopen函数的第一个参数类型是C格式字符串。
*/
FILE* file = fopen(filename.c_str(),"r");

8. find()bb80bcdcbcf246c3894ded0015662303.png

        find()作用就是查找字符或者字符串,从pos位置开始。默认情况下,pos位置是从0开始。

        rfind()作用与find几乎相同,不过是从字符串尾开始查找。

9. sub_str()

a978d148b9ef41aeba186d74d2d8ae3f.png

        在str中从pos位置开始,截取len个字符,将其作为字符串返回。如果len没有给或者大于npos(-1),含义则是从pos位置截取到字符串尾。

#include <iostream>
#include <string>int main ()
{std::string str="We think in generalities, but we live in details.";// (quoting Alfred N. Whitehead)std::string str2 = str.substr (3,5);     // "think"std::size_t pos = str.find("live");      // position of "live" in strstd::string str3 = str.substr (pos);     // get from "live" to the endstd::cout << str2 << ' ' << str3 << '\n';return 0;
}

📁 模拟实现string

        下面会有大量的string接口的模拟实现,模拟实现是为了更好的从里层理解string,使用string。

📂 拓展知识 :  编码

028e6539116b40fa82cc8b81324f4d7b.png

        常见的编码有:ASCII编码,GBK编码,UTF编码。

        所谓的编码,就是文字在计算机中的存储和表示。我们通过编码将计算机0 1 表示成日常生活中的文字。

        但随着越来越多国家的加入,简单的ASCII编码已经不能满足需求。所以有了UTF编码,UTF编码有UTF-8,UTG-16(2B),UTF-32(4B),它们主要的区别就是UTF-8是可变字节,utf-16和utf-32是不变字节。

        我们常用的string的底层就是使用utf-8的编码方式的char实例化的。

模拟实现string类,并完成测试namespace bit{class string{friend ostream& operator<<(ostream& _cout, const bit::string& s);friend istream& operator>>(istream& _cin, bit::string& s);public:typedef char* iterator;public:string(const char* str = "");string(const string& s);string& operator=(const string &s);~string();//// iteratoriterator begin();iterator end();/// modifyvoid push_back(char c);string& operator+=(char c);void append(const char* str);string& operator+=(const char* str);void clear();void swap(string& s);const char* c_str()const;/// capacitysize_t size()constsize_t capacity()constbool empty()constvoid resize(size_t n, char c = '\0');void reserve(size_t n);/// accesschar& operator[](size_t index);const char& operator[](size_t index)const;///relational operatorsbool operator<(const string& s);bool operator<=(const string& s);bool operator>(const string& s);bool operator>=(const string& s);bool operator==(const string& s);bool operator!=(const string& s);// 返回c在string中第一次出现的位置size_t find (char c, size_t pos = 0) const;// 返回子串s在string中第一次出现的位置size_t find (const char* s, size_t pos = 0) const;// 在pos位置上插入字符c/字符串str,并返回该字符的位置string& insert(size_t pos, char c);string& insert(size_t pos, const char* str);// 删除pos位置上的元素,并返回该元素的下一个位置string& erase(size_t pos, size_t len);private:char* _str;size_t _capacity;size_t _size;}};

📂 默认成员函数的模拟实现

//构造函数string(const char* str = ""):_size(strlen(str)){_str = new char[_size + 1];strcpy(_str, str);_capacity = _size;}//拷贝构造函数string(const string& s){string temp(s._str);swap(temp);}//赋值重载string& operator=(string temp){swap(temp);return *this;}//析构函数~string(){delete[] _str;_str = nullptr;_size = _capacity = 0;}

📂 iterator模拟实现

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

📂 insert 和 erase的模拟实现

//再字符串的pos位置插入字符string& insert(size_t pos, char ch){assert(pos <= _size);if (_size == _capacity){reserve(_capacity == 0? 4:2 * _capacity);}size_t end = _size + 1;while (end > pos){_str[end] = _str[end - 1];end--;}_str[end] = ch;_size++;return *this;}string& insert(size_t pos, const char* str){assert(pos <= _size);size_t len = strlen(str);if (_size + len > _capacity){reserve(_size + len);}size_t end = _size + len;while (end > pos+len-1){_str[end] = _str[end - len];end--;}strncpy(_str + pos, str, len);_size += len;return *this;}//删除pos位置后len个元素string& erase(size_t pos=0, size_t len=npos){assert(pos < _size);if (len >= _size - pos || len == npos){_str[pos] = '\0';_size = pos;return *this;}else{strcpy(_str + pos, _str + pos + len);_size -= len;return *this;}}

📂 modify模拟实现

//modifyvoid swap(string& s){std::swap(this->_str, s._str);std::swap(this->_size, s._size);std::swap(this->_capacity, s._capacity);}const char* c_str() const{return _str;}void clear(){_size = 0;_str[_size] = '\0';}void push_back(char ch){//扩容2倍/*if (_size == _capacity){reserve(_capacity == 0 ? 4 : 2 * _capacity);}_str[_size] = ch;_size++;_str[_size] = '\0';*/insert(_size, ch);}void append(const char* str){//扩容/*size_t len = strlen(str);if(_size >= _capacity - len){reserve(_size + len);}strcpy(_str + _size, str);_size += len;*/insert(_size, str);}string& operator+=(char ch){push_back(ch);return *this;}string& operator+=(const char* str){append(str);return *this;}

📂 capacity模拟实现

//capacitysize_t size() const{return _size;}size_t capacity() const {return _capacity;}bool empty() const{return _size == 0;}void reserve(size_t n){if (n > _capacity){char* temp = new char[n + 1];strcpy(temp, _str);delete[] _str;_str = temp;_capacity = n;}}void resize(size_t n, const char ch = '\0'){if (n <= _size){_str[n] = '\0';_size = n;}else{reserve(n);for (size_t i = _size;i < n;i++){_str[i] = ch;}_str[n] = '\0';_size = n;}}

📂  relational operators的模拟实现

//relational operatorsbool operator<(const 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 strcmp(this->c_str(), s.c_str()) == 0;}bool operator!=(const string& s){return !(*this == s);}

📂 find 的模拟实现

//返回字符c在字符串中出现的第一次位置size_t find(char ch, int pos = 0){assert(pos < _size);for (size_t i = pos;i < _size;i++){if (_str[i] == ch){return i;}}return npos;}//返回子串s在字符串中出现的第一次位置size_t find(const char* s, size_t pos = 0){assert(pos < _size);char* ps = strstr(_str + pos, s);if (ps != nullptr){return ps - _str;}else{return npos;}}

📂 substr的模拟实现

string substr(size_t pos=0, size_t len=npos){string temp;if (len == npos || _size - pos < len){for (size_t i = pos;i < _size;i++){temp += _str[i];}}else{for (size_t i = pos;i < pos + len;i++){temp += _str[i];}}return temp;}

📁 总结

        以上,我们就对string进行了全面的讲解,介绍了string的常用接口,string的底层实现,以及string相关的拓展知识。

        如果你能看到这里,恭喜你,你已经对string有了全面的了解,可以说已经上手了string。剩下的就是不断调试模拟实现string了,当然模拟实现只是为了更好的理解string。

        如果感觉本期内容对你有帮助,欢迎点赞,收藏,关注。Thanks♪(・ω・)ノ

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

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

相关文章

【uni-app】condition 启动模式配置,生产环境无效,仅开发期间生效

在小程序开发过程中&#xff0c;每次代码修改后&#xff0c;都会启动到首页&#xff0c;有时非常不方便&#xff0c;为了更高效的开发&#xff0c;有时需要模拟直接跳转到指定的页面&#xff0c; 操作方法如下&#xff1a; 在pages.joson里面配置下列代码&#xff1a; "…

解决 matplotlib 中文显示乱码的问题

matplotlib 库默认只显示中文 例如&#xff1a; import matplotlib.pyplot as pltimg plt.imread(test.jpg)# plt.rcParams[font.sans-serif] [SimHei] # 用来正常显示中文标签 # plt.rcParams[axes.unicode_minus] False # 用来正常显示负号 #有中文出现的情况&#xf…

宏auto关键字(C++基础)

宏 宏可以实现对语句的同义替换&#xff0c;简单来说就是预处理阶段、编译前的替换&#xff08;包括符号&#xff0c;变量等&#xff09;。 #define LOG(x) std::cout << x << std::endl; LOG("hello") 可以正常使用。 下面通过上图中借用不同开发模…

YOLOv8改进 | 独家创新篇 | 利用DCNv3集合DLKA形成全新的注意力机制(全网独家创新)

一、本文介绍 本文给大家带来的机制是由我独家创新结合Deformable Large Kernel Attention (D-LKA) 注意力机制和DCNv3可变形卷积的全新注意力机制模块(算是二次创新),D-LKA的基本原理是结合了大卷积核和可变形卷积的注意力机制,通过采用大卷积核来模拟类似自我关注的感受…

MySQL学习笔记(一)数据库事务隔离级别与多版本并发控制(MVCC)

一、数据库事务隔离级别 数据库事务的隔离级别有4种&#xff0c;由低到高分别为Read uncommitted &#xff08;读未提交&#xff09;、Read committed&#xff08;读提交&#xff09; 、Repeatable read&#xff08;可重复读&#xff09; 、Serializable &#xff08;串行化&a…

OpenAI劲敌吹新风! Claude 3正式发布,Claude3使用指南

Claude 3是什么&#xff1f; 是Anthropic 实验室近期推出的 Claude 3 大规模语言模型&#xff08;Large Language Model&#xff0c;LLM&#xff09;系列&#xff0c;代表了人工智能技术的一个显著飞跃。 该系列包括三个不同定位的子模型&#xff1a;Claude 3 Haiku、Claude 3…

06 - ip route和route -n的区别

1 ip route和route -n的区别 ip route 和 route -n 都是用于查看和管理Linux系统路由表的命令。但下面是它们的区别&#xff1a; ip route&#xff1a;是Linux系统中的现代工具&#xff0c;它属于iproute2套件&#xff1b;它提供了更多的选项&#xff0c;可以更精确地控制路由表…

弱电综合布线:连接现代生活的纽带

在当今信息化快速发展的时代&#xff0c;弱电网络布线作为信息传输的重要基础设施&#xff0c;其作用日益凸显。它不仅保障了数据的高效流通&#xff0c;还确保了通信的稳定性。从商业大厦到教育机构&#xff0c;从政府机关到医院急救中心&#xff0c;再到我们居住的社区&#…

EIP-1559

EIP EIP是以太坊改进提案&#xff08;Ethereum Improvement Proposal&#xff09;的缩写。它是一种标准化的提案制度&#xff0c;用于描述和讨论对以太坊区块链网络的改进和升级。EIP的目的是提供一个开放的、透明的过程&#xff0c;让社区成员、开发者和其他利益相关者能够共同…

短视频矩阵系统----矩阵系统源码搭建(技术门槛?)

短视频矩阵是什么意思&#xff1f;短视频矩阵的含义可以理解为全方位的短视频账号&#xff0c;通过不同的账号实现全方位的品牌展示。实际上是指一个短视频账号&#xff0c;通过不同的链接实现品牌展示&#xff0c;在不同的粉丝流量账号中互相转发同一个品牌&#xff0c;在主账…

pytorch CV入门3-预训练模型与迁移学习.md

专栏链接&#xff1a;https://blog.csdn.net/qq_33345365/category_12578430.html 初次编辑&#xff1a;2024/3/7&#xff1b;最后编辑&#xff1a;2024/3/8 参考网站-微软教程&#xff1a;https://learn.microsoft.com/en-us/training/modules/intro-computer-vision-pytorc…

外包干了8天,技术退步明显。。。。。

先说一下自己的情况&#xff0c;本科生&#xff0c;19年通过校招进入杭州某软件公司&#xff0c;干了接近3年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了四年的功能测试…

能源大数据采集,为您提供专业数据采集服务

随着经济的不断发展&#xff0c;能源产业也逐渐成为国民经济的支柱产业之一。而对于能源行业来说&#xff0c;数据采集是一项至关重要的工作。以往&#xff0c;能源企业采集数据主要依靠人工收集、整理&#xff0c;但是这种方式不仅效率低下&#xff0c;而且容易出现数据不准确…

Python测试框架Pytest的基础入门

Pytest简介 Pytest is a mature full-featured Python testing tool that helps you write better programs.The pytest framework makes it easy to write small tests, yet scales to support complex functional testing for applications and libraries. 通过官方网站介绍…

SpringBoot 手写 Starter

1.介绍 SpringBoot中的starter是一种非常重要的机制&#xff0c;能够抛弃以前繁杂的配置&#xff0c;将其统一集成进starter&#xff0c;应用者只需要在maven中引入starter依赖&#xff0c;SpringBoot就能自动扫描到要加载的信息并启动相应的默认配置。starter让我们摆脱了各种…

九型人格测试,8号领袖型人格的职业分析

8号人格&#xff0c;也叫领袖型人格&#xff0c;在九型人格中间&#xff0c;是一种天生领导的存在。他们生性开朗&#xff0c;能够和其他人建立良好的关系&#xff0c;为人不拘小节&#xff0c;遇强则强&#xff0c;坚守心中的理想和正义。不喜欢被人控制&#xff0c;喜欢自己当…

Wireshark——获取捕获流量的前N个数据包

1、问题 使用Wireshark捕获了大量的消息&#xff0c;但是只想要前面一部分。 2、方法 使用Wireshark捕获了近18w条消息&#xff0c;但只需要前5w条。 选择文件&#xff0c;导出特定分组。 输入需要保存的消息范围。如&#xff1a;1-50000。 保存即可。

LInux-多线程基础概念

文章目录 前言预备页表详解缺页中断页表的映射 一、多线程是什么&#xff1f;轻量级进程 二、Pthread库pthread_create 前言 从本章的多线程开始&#xff0c;我们开始进入Linux系统的尾声&#xff0c;所以&#xff0c;在学习多线程的过程中&#xff0c;我们也会逐步对之前的内…

自动化测试总结

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 最近要在新入职的公司准备一份自动化测试的培训&#xff0c;这是我在得知要做自动化测试培训以后…

深度学习系列61:在CPU上运行大模型

1. 快速版 1.1 llamafile https://github.com/Mozilla-Ocho/llamafile 直接下载就可以用&#xff0c;链接为&#xff1a;https://huggingface.co/jartine/llava-v1.5-7B-GGUF/resolve/main/llava-v1.5-7b-q4.llamafile?downloadtrue 启动&#xff1a;./llava-v1.5-7b-q4.lla…