标准库中的string

news2024/9/21 14:42:22

字符串容量头文件:<string>

string本质上就是字符顺序表;

class string
{
private:
	char* str;
	size_t _size;
	size_t capacity;
};

1. string类对象的常见构造 

(constructor) 函数名称
功能说明
string() (重点)
构造空的 string 类对象,即空字符串
string(const char* s) (重点)
C-string 来构造 string 类对象
string(size_t n, char c)
string 类对象中包含 n 个字符 c
string(const string&s) (重点)
拷贝构造函数

 无参:空的string

string s1;

带参:用一个常量字符串初始化string

string s1("hello world");

拷贝构造是深拷贝

string s3(s2);

2. string类对象的容量操作

C++为string提供了许多接口

函数名称
功能说明
size (重点)
返回字符串有效字符长度
length
返回字符串有效字符长度
capacity
返回空间总大小
empty (重点)
检测字符串释放为空串,是返回 true ,否则返回 false
clear (重点)
清空有效字符
reserve (重点)
为字符串预留空间(提前开辟)
resize (重点)
将有效字符的个数该成 n 个,多出的空间用字符 c 填充
size: 

返回字符串的长度(以字节为单位);
这是符合字符串内容的实际字节数,不一定等于其存储容量;(省去了"\0")

int main()
{
	string s("hello world");
	cout << s.size() << endl;//11,不算"\0"
	return 0;
}
length:

和size基本一样,但是size能包含的内容更大;

max_size

最大的size大小(不同容器,不同平台不同);

capacity:

(容量:开的空间)

返回已分配存储的大小

返回当前为字符串分配的存储空间的大小,以字节为单位表示;
容量不一定等于字符串长度。它可以相等或更大,当向字符串添加新字符时,额外的空间允许对象优化其操作;(显示的是有效字符个数,不算"\0")

接下来,我们看看在VS下是如何扩容的:

void TestPushBack()
{
	string s;
	size_t sz = s.capacity();
	cout << "first_capacity:" << sz << endl;
	cout << "making s grow:\n";
	for (int i = 0; i < 100; ++i)
	{
		s.push_back('c');
		if (sz != s.capacity())
		{
			sz = s.capacity();
			cout << "capacity changed: " << sz << endl;
		}
	}
}

在VS中设计:当数据个数小于16时,会先存在内部的一个_buff里边 (VS防止小块内存在堆上)

微软工程师实现:

class string
{
private:
	char _buff[16];//n<16,这也是为什么第一次按2倍扩的原因
	char* str;//n>=16时,_buff就废弃了,全部开在了str指向的堆上
	size_t _size;
	size_t capacity;
};

 但是在不同平台有着不同的实现方式:

g++string的结构(标准的2倍扩容,没有_buff)

G++下,string是通过写时拷贝实现的,string对象总共占4个字节,内部只包含了一个指针,该指针将来指向一块堆空间,内部包含了如下字段:

空间总大小

字符串有效长度

引用计数

指向堆空间的指针,用来存储字符串

struct _Rep_base
{
size_type _M_length;
size_type _M_capacity;
_Atomic_word _M_refcount;
};

 频繁的扩容是代价的,拷贝数据-释放空间,会降低效率,那我们如何避免扩容,减少扩容呢?

解决:reserve,resize

reserve:

请求将字符串容量调整为计划的大小更改,则该函数会导致容器将其容量增加到 n 个字符(或更大);

在所有其他情况下,它被视为一个非约束性请求,以缩小字符串容量:容器实现可以自由地进行优化,并使大于 n;(节省空间,但一般不会缩容(根据不同平台:VS不缩容(其实_buff就可以看出来了),g++缩容),因为缩容是要付出代价的,因为待会需要空间的话还要继续扩)

参考:典型的缩容:shrink_to_fit :capacity减到size

此函数对字符串容量没有影响,并且不能更改其内容;

reserve开的大小不包含\0,实际上最少要开n+1;

作用:提前开空间,避免扩容,提高效率

void TestPushBack()
{
	string s;
	s.reserve(100);
	size_t sz = s.capacity();
}
resize:

(扩容插入字符) 

void resize (size_t n);void resize (size_t n, char c);

将字符串的大小调整为 n 个字符的长度;

如果 n 小于当前字符串长度,则当前值将缩短为其前 n 个字符,并删除第 n个字符之外的字符;

如果 n 大于当前字符串长度,则通过在末尾插入所需数量的字符来扩展当前内容,以达到 n 的大小。如果指定了 c,则新元素将初始化为 c 的副本,否则,它们是值初始化的字符(null 字符); 

size_t _sz = s.size();
s.resize(_sz + 2, '+');
clear:
void clear();

清除字符串

擦除字符串的内容,该字符串将变为空字符串(长度为 0 个字符)。

clear一般是只清理内容,不清除掉容量的(怕白干了),但个别少数会连容量(空间)一起清除的

s.clear();
empty:

(判空)

bool empty() const;

测试字符串是否为空

返回字符串是否为空(即其长度是否为 0);
此函数不会以任何方式修改字符串的值;

cout << s.empty() << endl;

注意:

1. size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一致,一般情况下基本都是用size();

2. clear()只是将string中有效字符清空,不改变底层空间大小;

3. resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字符个数增多时:resize(n)用0来填充多出的元素空间,resize(size_t n, charc)用字符c来填充多出的元素空间。注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变;

4. reserve(size_t  res_arg=0):为string预留空间,不改变有效元素个数,当reserve的参数小于string的底层空间总大小时,reserver不会改变容量大小;

3. string类对象的访问及遍历操作

函数名称
功能说明
operator[ ] (重点)
返回 pos 位置的字符, const string 类对象调用
begin + end
begin 获取一个字符的迭代器 + end 获取最后一个字符下一个位
置的迭代器
rbegin + rend
begin 获取一个字符的迭代器 + end 获取最后一个字符下一个位
置的迭代器
范围 for
C++11 支持更简洁的范围 for 的新遍历方式

string::iterator(正向迭代器)(string::const_iterator)

begin:

返回一个迭代器,该迭代器指向字符串的第一个字符;

如果字符串对象是 const 限定的,则该函数返回const_iterator。否则,它将返回一个迭代器;
成员类型 iterator 和 const_iterator 是随机访问迭代器类型(分别指向字符和 const 字符);

string::iterator it = s2.begin();

end:

返回一个迭代器,该迭代器指向字符串的末尾字符("\0")

string::iterator it = s2.end();

string::reverse_iterator(反向迭代器) (string::const_reverse_iterator)

rbegin:

返回反向迭代器以反向开始

返回一个反向迭代器,指向字符串的最后一个字符(即其反向开头)(有效字符);
反向迭代器向后迭代:增加迭代器会使它们朝向字符串的开头;(++倒着走)
rbegin 指向 member end 将指向的字符之前的字符;

string::iterator it = s2.rbegin();

rend;

将反向迭代器返回到反向端

返回一个反向迭代器,指向字符串的第一个字符(被视为其反面端)之前的理论元素;(也就是第一个字符的前一个位置);

string::rbegin 和 string::rend 之间的范围包字符串的所有字符(顺序相反)(左(rbegin)闭右(rend)开);

operator[ ]

获取字符串的字符

返回字符串中位置 pos 处的字符的引用

1.pos是size_t类型; 

2.pos在字符串中是从0开始,即:第一个字符位置用0表示;

3.如果字符串对象是 const 限定的,则该函数返回 const char&,否则,它将返回 char&;

因此,我们可以去访问字符串中的字符:(适用于数组)

s2[0] = 'x';
//下标+[]
for (size_t i = 0; i < s2.size(); i++)
{
	cout << s2[i] << " ";
}

string是支持迭代器的方式进行遍历,迭代器是STL的六大组件之一,迭代器是用来遍历访问容器的;(所有的容器都可以用迭代器访问)

//迭代器
string::iterator it = s2.begin();
while (it != s2.end())
{
	cout << *it << " ";
	it++;
}

//范围for:所有容器都支持
//从s2中取字符给ch
//本质是迭代器
for (auto ch : s2)
{
	ch += 2;
	cout << ch << " ";
}

反向迭代器:(反向访问)

4.string提供的库---Function

库 (点击进入详情页)

stoi:
int stoi (const string&  str, size_t* idx = 0, int base = 10);
int stoi (const wstring& str, size_t* idx = 0, int base = 10);

将字符串转换为整数

解析 str,将其内容解释为指定基数的整数,该整数作为 int 值返回;
如果 idx 不是 null 指针,则该函数还会将 idx 的值设置为 str 中第一个字符在数字之后的位置;

str:

表示整数的 String 对象

idx:

指向 size_t 类型的对象的指针,该对象的值由函数设置为 str 中下一个字符在数值之后的位置;
此参数也可以是 null 指针,在这种情况下,不会使用它;

base:

确定有效字符及其解释的数字基数(基数);
如果此值为 0,则使用的基数由序列中的格式确定;

请注意,默认情况下,此参数为 10,而不是 0

类似的:

long long x=stoll(num1);
to_string:
string to_string (int val);
string to_string (long val);
string to_string (long long val);
string to_string (unsigned val);
string to_string (unsigned long val);
string to_string (unsigned long long val);
string to_string (float val);
string to_string (double val);
string to_string (long double val);

将数值转换为字符串

返回一个表示为 val 的字符串

 类似:

5. string类对象的修改操作

函数名称
功能说明
push_back
在字符串后尾插字符 c
append
在字符串后追加一个字符串
operator+= ( 重点)
在字符串后追加字符串 str
c_str ( 重点 )
返回 C 格式字符串(兼容C)
find + npos ( 重点)
从字符串 pos 位置开始往后找字符 c ,返回该字符在字符串中的
位置
rfind
从字符串 pos 位置开始往前找字符 c ,返回该字符在字符串中的
位置
substr
str 中从 pos 位置开始,截取 n 个字符,然后将其返回(比copy好点,还要开buffer)
push_back:

将字符追加到字符串

将字符 c 追加到字符串的末尾,使其长度增加 1

append:

追加到字符串

通过在字符串的当前值末尾追加其他字符来扩展字符串

operator+=:

就是push_back和append的结合,很香的,可以丢掉前俩个(底层是按扩容的逻辑走的)

以后尾插用+=就对了

void test_string5()
{
	string s("hello world");
	s.push_back(' ');
	cout << s << endl;
	s.append("!!!");
	cout << s << endl;
	s += "1314";
	cout << s << endl;
	s += '!';
	cout << s << endl;
}
补充:
insert

说到尾插,还有头插,实际string没有头插,但是可以用insert接口实现头插:

s.insert(0, "hello everyone");

但头部,中间插入应该谨慎使用,有效率的流失

还有:

//插入一个
s.insert(0, "h");
s.insert(0, 1, 'h');//区别(小细节)
s.insert(0, 'h');//报错

由于历史的原因,整个string设计比较冗余,但是我们要尊重历史

语言都是向前兼容的,但python比较大胆~~~👍

erase

erase就比较好(指定位置删除字符)

从字符串中删除字符

擦除字符串的一部分,减小其长度:(头删就不用提供其他的接口)

void test_string6()
{
	string s("hello aaworld");
	s.erase(6, 2);//想要全删,默认给缺省值就行
	cout << s << endl;
}

其迭代器(接口)也支持头,尾删除(pop_back也有尾删功能):

//头删
s.erase(s.begin());
//还可以
s.erase(0, 1);
//尾删
s.erase(--s.end());
//还可以
s.erase(s.size() - 1, 1);
//都是间接实现
replace:

如果想把字符串的某一个位置替换成另外一个字符/字符串,可以用replace(本质:插入删除)(效率也不高)

string s("hello world");
s.replace(5, 1, "%%");//换一个就行,换两个就把1换成2
find and npos:

查找字符:(返回类型:size_t)(找到返回下标,没找到返回npos(npos是静态成员变量,要制定类域))

(将所有空格转换成'!'):

void test_string7()
{
	string s("hello world hello everyone nice to meet you   ");
	size_t pos = s.find(' ');
	while (pos != string::npos)
	{
		s.replace(pos, 1, "!");
		pos = s.find(' ', pos + 1);//防止对空格的从头开始查找
	}
	cout << s << endl;
}

但是面对大量数据是效率极低的,不可行的

void test_string7()
{
	string s("hello                       world ");
	size_t pos = s.find(' ');
	while (pos != string::npos)
	{
		s.replace(pos, 1, "!");
		cout << s << endl;
		pos = s.find(' ', pos + 1);//防止对空格的从头开始查找
	}
	cout << s << endl;
}

那该如何解决呢,我们可以拿空间换时间,又简单又高效:

void test_string7()
{
	string s("hello                       world ");
	string tmp;//牺牲空间
	tmp.reserve(s.size());//减少/避免 扩容
	for (auto ch : s)
	{
		if (ch == ' ')
		{
			tmp += "!";
		}
		else
		{
			tmp += ch;
		}
	}
	swap(s, tmp);//swap更高效
	cout << s << endl;
}

 5. string类非成员函数

函数名称
功能说明
operator+
尽量少用,因为传值返回,导致深拷贝效率低c
operator>> (重点)
输入运算符重载
operator<<  (重点)
输出运算符重载
getline (重点)
获取一行字符串
relational operators (重点)
大小比较

operator+

string+字符串:

string s1("hello ");
string s2 = s1 + "world";

字符串+string: (这样就不能重载为成员函数了,左操作数被string牢牢占用了)(只能重载为全局的,跟流插入一样)

string s1("hello ");
string s2 = "world" + s1;
getline:

在做一些题目的时候,我们时时需要输入空格,但是cin会将空格/换行默认为输入分割的标志,因此,getline很好的为我们解决了这个问题

getline:遇到空格不终止,还是从缓冲区拿数据,默认是换行才是分割的标志

string str;
getline(cin,str);

也可以遇到自定的符号作为结束的标志:(现在不默认了)

string str;
getline(cin,str,'*');

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

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

相关文章

为你的世界,开一扇任意门(上篇)

一、每个侠客心中都有一扇任意门 童年时代就喜欢《多来A梦》&#xff0c;特比羡慕和渴望有一扇任意门&#xff0c;可以穿越到目的地&#xff0c;真正做到虽万里路&#xff0c;咫尺间。 随着技术及姿势的普及&#xff0c;基本有点常识的朋友都知道快捷方式&#xff0c;再略微熟…

手机三要素接口怎么对接呢?(一)

一、什么是手机三要素&#xff1f; 手机三要素又叫运营商三要素&#xff0c;运营商实名认证&#xff0c;运营商实名核验&#xff0c;手机三要素实名验证&#xff0c;手机三要素实名核验&#xff0c;每个人的称呼都不同&#xff0c;但是入参和出参是一样的。 输入姓名、身份证…

谁骑车没点心情件呀|VELO Skin Wrap把带,你的骑行显眼带!

夏日的热浪在召唤&#xff0c;是时候给你的骑行加点色彩&#xff0c;添点心情件呀&#xff01;夏季骑行&#xff0c;最怕的就是手心湿漉漉的不适&#xff0c;由此生发&#xff0c;VELO Skin Wrap系列把带&#xff0c;以天鹅绒材质精心打造&#xff0c;如同指尖上的夏日微风&…

MES系统在机床产业智能化的作用

MES系统&#xff08;Manufacturing Execution System&#xff0c;制造执行系统&#xff09;在机床产业智能化过程中发挥着至关重要的作用。以下是万界星空科技MES系统在机床产业智能化中的几个关键作用&#xff1a; 1. 实时数据采集与分析 数据采集&#xff1a;MES系统通过与生…

人工智能和计算机视觉领域国际学术会议submission

文章目录 1. AAAI 20252. CVPR 20253. ICCV 20254. IJCAI 20255. ICRA 20256. NeurIPS 20257. ACL 20258. ICLR 2025 1. AAAI 2025 人工智能促进协会&#xff08;AAAI&#xff09;是一个成立于1979年的非营利性科学组织&#xff0c;专注于深化对智能行为和思维机制的科学理解&…

暄桐好作业之《临沈周〈东庄图册〉局部》

暄桐是一间传统美学教育教室&#xff0c;创办于2011年&#xff0c;林曦是创办人和授课老师&#xff0c;教授以书法为主的传统文化和技艺&#xff0c;皆在以书法为起点&#xff0c;亲近中国传统之美&#xff0c;以实践和所得&#xff0c;滋养当下生活。      其中“暄桐好作…

八股文无用?也许是计算机大学生的重要人生指南!

大家所说的"八股文"其实指的是那些固定、标准化的面试问题和答案&#xff0c;通常涉及特定的知识点和技术概念。 博主本人也是一枚大学生&#xff0c;个人也记背过相关的八股文&#xff0c;比如计算机网络里的TCP和UDP的区别、TCP三次握手和四次挥手的具体过程等等&a…

ADS环境下的ARM汇编程序设计实验报告

ADS环境下的ARM汇编程序 一、 实验目的 1&#xff0e;了解 ARM汇编语言的基本框架,学会使用ARM的汇编语言编程。 2&#xff0e;熟悉ADS1.2下进行汇编语言程序设计的基本流程&#xff1b; 3. 了解AXD中调试功能。 二、 实验环境 硬件&#xff1a;PC机 软件&#xff1a;ADS…

释放群众的创造力:打赢网络犯罪战争

关注公众号网络研究观获取更多内容。 企业业非但没有享受到技术带来的好处&#xff0c;反而承受着高昂的成本。 事情不应该是这样的。 企业投资 IT 是因为相信它可以改变他们与客户的关系、供应链和业务敏捷性。在过去 12 个月中&#xff0c;我们还看到人们对 AI 如何增强…

linux深度学习环境配置(cuda,pytorch)

显卡驱动 首先查看linux服务器是否存在显卡驱动&#xff0c;可以输入以下命令 nvidia-smi如果没有直接显示下面的画面 则进行下面的步骤&#xff1a; ubuntu-drivers devices sudo ubuntu-drivers autoinstall上述步骤的意思是直接在线安装 然后重启linux服务器 reboot发现…

mysql 性能调优概述

1.查看执行计划 id&#xff1a;操作表顺序。 id不同&#xff0c;执行顺序从大到小&#xff0c;id相同&#xff0c;执行顺序从上到下。 select_type&#xff1a;select类型 Simple&#xff1a;简单查询&#xff0c;不包含子查询或union Primary&#xff1a;最外层的查询 Su…

c->c++(三):stl

本文主要探讨c的stl相关知识:模版&#xff0c;容器&#xff0c;泛型算法&#xff0c;萃取特化&#xff0c;智能指针等。 模版 模板typename和class均可定义 模板参数可是类型,还可是值 模板编译根据调用实参类型推导参数类型 编译器用值的类型…

个人做量化交易一定不靠谱?

在某乎上的『量化』话题下&#xff0c;有一类关于个人量化交易者的问题很热门&#xff0c;比如『个人做量化交易靠不靠谱吗&#xff1f;』、『个人做量化交易到底可不可行&#xff1f;』、『个人做量化没啥优势&#xff0c;不如买量化基金&#xff1f;』。 关注度高的&#xff…

2025年穿戴甲全球市场预测与分析

传统美甲一直是大美业里的为数不多的“稳定”型项目&#xff0c;无论是产品、技术还是市场规模。直到2020年开始&#xff0c;穿戴甲的出现&#xff0c;打破了这一平衡生态&#xff0c;究其原因还是因为&#xff1a;创新&#xff01;通常我们在判断一个创新型产品或者服务的出现…

远程访问mysql数据库的正确打开方式

为了安全&#xff0c;mysql数据库默认只能本机登录&#xff0c;但是在有些时候&#xff0c;我们会有远程登录mysql数据库的需求&#xff0c;这时候应该怎么办呢&#xff1f; 远程访问mysql数据&#xff0c;需要两个条件&#xff1a; 首先需要mysql服务器将服务绑定到0.0.0.0…

LazyLLM:长上下文场景下提高LLM推理效率

LazyLLM旨在优化大型语言模型&#xff08;LLM&#xff09;在处理长文本语境下的推理效率。传统上&#xff0c;LLM的推理过程分为预填充和解码两个阶段&#xff0c;其中预填充阶段负责计算并存储输入提示的所有token的键值&#xff08;KV&#xff09;缓存&#xff0c;这一步骤在…

转转上门履约服务拆分库表迁移实践

文章目录 1 背景2 数据迁移方案2.1 方案一&#xff1a;双写新旧库2.2 方案二&#xff1a;灰度开关切换新旧库 3 迁移细节3.1 业务代码改造3.2 数据同步3.3 数据一致性校验 4 总结5 参考资料 1 背景 随着业务不断发展&#xff0c;一个服务中部分功能模块适合沉淀下来作为通用的…

OverlayFS 文件系统介绍

引言 OverlayFS&#xff08;Overlay Filesystem&#xff09;是 Linux 内核中的一种联合文件系统&#xff08;Union Filesystem&#xff09;&#xff0c;它通过叠加多个目录形成一个单一的文件系统视图。作为 Docker 的默认存储驱动之一&#xff0c;OverlayFS 在提高性能和简化容…

Python 装饰器简单使用

在Python编程中&#xff0c;装饰器&#xff08;Decorators&#xff09;是一种强大且优雅的功能&#xff0c;它允许我们在不修改原有函数代码的情况下&#xff0c;给函数增加新的功能。装饰器本质上是一个函数&#xff0c;它接收一个函数作为参数并返回一个新的函数&#xff0c;…

【医疗大数据】健康分析法应用于商业领域的文献回顾

这几天在看医疗大数据的文章&#xff0c;找到了这篇关于健康分析学在商业领域的应用&#xff0c;概括性地探讨了通过医疗大数据来解决医疗领域的问题。 Health analytics in business research: a literature review 1、研究背景&#xff1a; 本文探讨了健康分析学&#xff0…