STL--string



🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉


1、什么是STL

STL(standard template libaray-标准模板库):是C++标准库的重要组成部分,不仅是一个可复用的组件库,而且 是一个包罗数据结构与算法的软件框架。

1.1、STL的六大组成

2、string

2.1、string文档介绍

默认成员函数

迭代器

容量相关的

C语言中:string

字符数组,可以扩容,可以增删改查。(更符合的信息都是字符串存的,比如身份证,名字,地址**...**)

数据和方法分离(空间和使用)

C++中:string

2.2、string的构造

提供了7个

2.3、string类对象的常见构造

就前3个常用,其余的不常用。

带参构造、无参构造、拷贝构造

关于npos

size_t表示无符号整形,所以npos为FFFF FFFFH

2.4、string类对象的容量操作

知识点

operator[]

底层:

一个指针,一个大小,一个容量(用来扩容)。

2.5、我们如何遍历自定义类型的字符串呢?

方法1:size( ) + operator[ ]

这里自定义类型s1可以像数组一样访问,而且访问越界有报错。

cpp 复制代码
void test_string3()
{
	string s1("hello world");

	//计算s1大小
	cout << s1.size() << endl;


	//访问字符串
	// 方法1:operator[]
	//自定义类型s1,可以像数组一样访问
	//可读,可写,可修改
	
	//可读
	for (size_t i = 0; i < s1.size(); i++)
	{
		//写全
		//cout << s1.operator[i] << " ";
		cout << s1[i] << " ";
	}
	cout << endl;

	s1[0] = 'x';
	cout << endl;
	
	//可修改
	for (size_t i = 0; i < s1.size(); i++)
	{
		s1[i]++;
	}
}

放法2:迭代器

先看代码👇👇👇

cpp 复制代码
void test_string4()
{
	string s1("hello world");

	//iterator定义在类域里,只能在类域中搜索。
	string::iterator it1 = s1.begin();
	while (it1 != s1.end())
	{
		cout << *it1 << " ";
		++it1;
	}
	cout << endl;
}

代码中出现begin( ) 和 end( )

如何理解呢?

解析:

我们可以把begin和end看作指针 ,**begin指第一个字符,end指最后一个字符的下一个位置。**结合代码,begin把第一个字符的地址给给it1,然后解引用it1取到值,++it1走到下一个位置,知道遇到end()就停止。

注意:

\0是表示字符,不是有效字符

[ begin,end ) 为左闭右开区间,

对比方法一和方法二:

方法一只适合下标访问,空间必须是连续的,所以只适用于string;

方法二迭代器是主流的访问方式,

举例链表使用迭代器访问

总结:

begin( ):任何容器返回第一个数据位置的iterator

end( ):任何容器返回最后数据的下一个位置的iterator

方法3:范围for(所有容器都支持)

cpp 复制代码
​
for (auto e : s1)
{
	cout << e << " ";

}
cout << endl;

​

自动取s1里的值,赋值给e,自动++,自动判断结束。

底层角度,就是迭代器

注意:

这里是*it赋值给给e,赋值拷贝,并不会影响数据。

2.6迭代器iterator

普通迭代器const迭代器

我们先看一下const迭代器

如图,这里是一个const string,就不能用之前那种方式遍历了,

因为const string 是只读的,这里我们调用

修改代码:👇👇👇

总结:

iterator 可读可写

const_iterator 只读

普通的迭代是给普通的string用的

const的是给const对象用的,保护string不能修改数据,可以遍历

这里我们再深究一下:

为什么const_iterator这洋写,而不是const iterator

因为const iterator 保证的是迭代器本身不能写

而const_operator 保证的是迭代器指向的数据不能写

反向迭代器

rbeginrend

均只读

代码:👇👇👇

cpp 复制代码
string s2("hello world");
string::reverse_iterator it2 = s2.rbegin();
while (it2 != s2.rend())
{
	cout << *it2 << " ";
	++it2;
}
cout << endl;

底层重载operator++来实现自定义类型的++。

迭代器总结

共有4中迭代器

iterator const_iterator reverse_iterator const_reverse_iterator

差异就在读和写部分

iterator 可读可写

const_iterator只读

reverse_iterator 只读

const_reverse_iterator 只读

2.7string插入字符

​​​​​​​​​​​​​​​​​​​​​pushappend

插入字符

但这两种方式都不常用

还是喜欢用重载运算符

2.7.1assign

assign:赋值字符串,但赋值通常用operator=,运算符重载。

这里我们浅浅了解一下,熟悉一下看文档。

2.7.2insert、erase和repalce

insert插入

string的insert慎用,

因为时间复杂度很大,效率低。

库中没有给出头插一个字符怎某写,

这里我们这样:👇👇👇

cpp 复制代码
char ch = 'y';
cin >> ch;
s2.insert(0, 1, ch);//头插一个
cout << s2 << endl;

insert的使用:👇👇👇

cpp 复制代码
void test_string8()
{
	string s1("hello world");
	cout << s1 << endl;

	s1.assign("11111");
	cout << s1 << endl;

	string s2("hello world");
	s2.insert(0, "xxxx");
	cout << s2 << endl;

	
	char ch = 'y';
	cin >> ch;
	s2.insert(0, 1, ch);//头插一个
	cout << s2 << endl;

	s2.insert(s2.begin(), 'y');
	cout << s2 << endl;

	s2.insert(s2.begin(), s1.begin(), s1.end());//把s1的头到尾,插入s2
	cout << s2 << endl;
}

erase和replace

erase:

慎用,效率低。

replace:

把我的一部分替换

erase的使用👇👇👇

cpp 复制代码
void test_string9()
{
	string s1("hello world");
	cout << s1 << endl;

	s1.erase(0, 1);//去掉第一个字符
	cout << s1 << endl;

	s1.erase(5, 100);//去掉5 - 100,后边没有也不报错,
	cout << s1 << endl;

	//replace效率不高,慎用,和insert类似,需要挪动数据
	string s2("hello world");
	s2.replace(5, 1, "%20");
	cout << s2 << endl;
}

说到替换,那把所有空格都替换怎某实现呢?

代码👇👇👇

cpp 复制代码
//把空格全部替换成%20
//方法一:循环替换,但时间复杂度大
string s3("hello world hello bit");
for (size_t i = 0; i < s3.size();)
{
	if (s3[i] == ' ')
	{
		s3.replace(i, 1, "%20");
		i += 3;//跳过%20这三个字符
	}
	else
	{
		i++;
	}
}
cout << s3 << endl;
//方法二:以空间换时间,
string s4("hello world hello bit");
string s5;
for (auto ch : s4)
{
	if (ch != ' ')
	{
		s5 += ch;
	}
	else
	{
		s5 += "%20";
	}
}
cout << s5 << endl;

这里提供了俩方法:

1.循环替换

2.另辟蹊径

我们这里不推荐方法一循环替换,为什么呢?

因为时间复杂度非常大,方法二以时间换空间。

看到这里,我们小试牛刀,练练手。

小试牛刀

.仅仅反转字母(双指针)

.字符串中的第一个唯一一个字符(映射)

.验证回文串(回文:对称),题目:只判断字母

2.7.3max_size()

字符串的最大长度(用的不多),因为类型开不出这样大的空间

2.7.4reserve和resize

reserve改变capacity

resize改变size,也可以改变capacity

reserve:扩容(提前开好空间,不适用于缩容)

在vs中不能缩容(看编译器)

cpp 复制代码
string s1("11111111111");
string s2("1111111111111111111111111111111");

cout << s1.capacity() << endl;

s1.reserve(100);
cout << s1.capacity() << endl;
s1.reserve(20);
cout << s1.capacity() << endl;

reserve的使用

cpp 复制代码
void TesePushBack()
{
	string s;
	s.reserve(200);
	s[100] = 'x';
}

开了空间是不能直接赋值的,因为[ ]在底层调用operator [ ],会调用size,而reverse只改变capacity,所以这时还需要初始化size

resize的使用

cpp 复制代码
void test_string11()
{
	string s1;
	s1.resize(5);
	s1[4] = '3';
	s1[3] = '4';
	s1[2] = '5';
	s1[1] = '6';
	s1[0] = '7';
}

resize不会填充前面已有数据,会在后面补

cpp 复制代码
string s2("hello world");
s2.resize(20, 'x');

2.7.5 at

越界,异常可以捕获。

cpp 复制代码
string s2("hello world");

//s2[30];
//s2.at(30);
try
{
	s2.at(30);
}
catch (const exception& e)
{
	cout << e.what() << endl;
}

s2[30] 直接报错,

s2.at(30)会捕获异常

operator是暴力型的,at是温柔型的。

2.7.6 c_str()

文件读取

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS 1

string file("Test.cpp");
FILE* fout = fopen(file.c_str(), "r");//获取底层指向char的那个指针。
char ch = fgetc(fout);
while (ch != EOF)
{
	cout << ch;
	ch = fgetc(fout);
}

2.7.7 find()和rfind()

find()

查找字符

这里掌握第一个和第四个

第一个:查找一个striing,pos位置

第四个:从pos位开始找,一个字符
find() : 正在查找

rfind():倒着查找

3.vs和g++下string结构的说明

3.1vs下

注意:下述结构是在32位平台下进行验证,32位平台下指针占4个字节。
vs下string的结构 string总共占28个字节,内部结构稍微复杂一点,先是有一个联合体,联合体用来定义string中字。

符串的存储空间:

当字符串长度小于16时,使用内部固定的字符数组来存放

当字符串长度大于等于16时,从堆上开辟空间

这种设计也是有一定道理的,大多数情况下字符串的长度都小于16,那string对象创建好之后,内部已经有了16个字符数组的固定空间,不需要通过堆创建,效率高。

其次:还有一个size_t字段保存字符串长度,一个size_t字段保存从堆上开辟空间总的容量

最后:还有一个指针做一些其他事情。

3.2g++下

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

内部包含了如下字段:

·空间总大小

·字符串有效长度

·引用计数

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


🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉


last but not least,创作不易,望读者三连三连三连支持💖

重要的事情说三遍💖

相关推荐
工业3D_大熊13 分钟前
3D可视化引擎HOOPS Luminate场景图详解:形状的创建、销毁与管理
java·c++·3d·docker·c#·制造·数据可视化
暮色_年华27 分钟前
Modern Effective C++ Item 11:优先考虑使用deleted函数而非使用未定义的私有声明
c++
流星白龙30 分钟前
【C++习题】10.反转字符串中的单词 lll
开发语言·c++
Smile丶凉轩1 小时前
微服务即时通讯系统的实现(服务端)----(1)
c++·git·微服务·github
萝卜兽编程1 小时前
优先级队列
c++·算法
珹洺3 小时前
C语言数据结构——详细讲解 双链表
c语言·开发语言·网络·数据结构·c++·算法·leetcode
孙同学要努力3 小时前
C++知识整理day1——前置基础知识整理(命名空间、输入输出、函数重载、引用)
开发语言·c++
沐泽Mu3 小时前
嵌入式学习-C嘎嘎-Day05
开发语言·c++·学习
几窗花鸢3 小时前
力扣面试经典 150(下)
数据结构·c++·算法·leetcode
Beau_Will3 小时前
数据结构-树状数组专题(1)
数据结构·c++·算法