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

stl最主要的是学习理解,能够自己仿实现stl的功能。
string
C语言中,字符串是以'\0'结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列
的库函数,但是这些库函数与字符串是分离开的,不太符合OOP的思想,而且底层空间需要用户
自己管理,稍不留神可能还会越界访问
在使用string类时,必须包含**#include头文件** 以及using namespace std;
string的成员函数了解
string是个可动态镇长的字符数组,下面是他默认的成员函数
1:构造 / 析构 / 赋值,老生常谈的东西了。这里就不详细展开解释了

2:迭代器相关成员函数
|-----------|--------------------------------------------------------------|
| begin | 返回指向容器起始位置的普通迭代器,可用于修改元素。 |
| end | 返回指向容器末尾位置(最后一个元素的下一个位置)的普通迭代器。 |
| rbegin | 返回指向容器反向起始位置(即普通末尾位置)的反向迭代器,用于反向遍历。 |
| rend | 返回指向容器反向末尾位置(即普通起始位置的前一个位置)的反向迭代器。 |
| cbegin | 返回指向容器起始位置的常量迭代器 (const_iterator),仅用于读取元素,不可修改。 |
| cend | 返回指向容器末尾位置的常量迭代器。 |
| crbegin | 返回指向容器反向起始位置的常量反向迭代器 (const_reverse_iterator),仅用于读取元素。 |
| crend | 返回指向容器反向末尾位置的常量反向迭代器。 |
3:容量管理相关的成员函数列表
|-------------------|----------------------------------------------------------------------------|
| size / length | 两者功能完全相同,返回字符串的有效字符长度 (不包含结尾的 \0)。 |
| max_size | 返回字符串理论上能达到的最大长度(受系统内存限制,一般仅作参考)。 |
| resize | 调整字符串的长度:若新长度大于原长度,会用指定字符(默认 \0)填充;若小于原长度,会截断多余字符。 |
| capacity | 返回字符串当前已分配的内存容量 (即实际占用的内存大小,通常大于等于 size)。 |
| reserve | 预分配内存容量:若传入的容量大于当前 capacity,会扩容到该容量;若小于,则无操作(不缩容)。常用于提前预留内存,减少后续扩容的性能开销。 |
| clear | 清空字符串,使其长度变为 0(但已分配的内存容量 capacity 不变)。 |
| empty | 判断字符串是否为空(长度为 0 时返回 true,否则返回 false)。 |
| shrink_to_fit | (C++11 新增)将已分配的内存容量收缩到与有效长度 size 一致,用于释放多余的内存空间。 |
4:元素访问相关的成员函数列表
|--------------|--------------------------------------------------------|
| operator[] | 通过下标访问字符串中的字符,不做越界检查(若下标非法,行为未定义)。 |
| at | 通过下标访问字符串中的字符,会做越界检查 (若下标非法,抛出 out_of_range 异常)。 |
| back | (C++11 新增)返回字符串的最后一个字符(若字符串为空,行为未定义)。 |
| front | (C++11 新增)返回字符串的第一个字符(若字符串为空,行为未定义)。 |
5:修改操作相关的成员函数列表
|--------------|----------------------------------------------------|
| operator+= | 字符串拼接,将右侧字符串追加到当前字符串末尾。 |
| append | 字符串拼接,功能与 operator+= 类似,支持更灵活的拼接形式(如拼接子串、多个字符等)。 |
| push_back | 向字符串末尾追加一个字符。 |
| assign | 给字符串赋值,可替换原有内容(支持赋值字符串、子串、多个字符等形式)。 |
| insert | 在字符串的指定位置插入字符或子串。 |
| erase | 删除字符串中指定位置的字符或子串。 |
| replace | 替换字符串中指定范围的子串为新内容。 |
| swap | 交换两个字符串的内容。 |
| pop_back | (C++11 新增)删除字符串的最后一个字符。 |
6:字符串操作相关的成员函数列表
|---------------------|--------------------------------------------------------------------------------|
| c_str | 返回与 string 等价的 C 风格字符串(以 \0 结尾的 char*),用于兼容 C 语言接口。 |
| data | 返回字符串的字符数据指针(const char*),功能与 c_str 类似,但不保证以 \0 结尾(实际中多数实现与 c_str 一致)。 |
| get_allocator | 获取 string 内部使用的内存分配器(一般用于底层内存管理,业务代码很少用到)。 |
| copy | 将字符串的字符序列拷贝到指定的字符数组中。 |
| find | 从前往后查找子串或字符,返回首次出现的位置;若未找到,返回 string::npos。 |
| rfind | 从后往前查找子串或字符,返回最后一次出现的位置;若未找到,返回 string::npos。 |
| find_first_of | 查找字符串中首次出现的目标字符集中的任意字符,返回其位置。 |
| find_last_of | 查找字符串中最后一次出现的目标字符集中的任意字符,返回其位置。 |
| find_first_not_of | 查找字符串中首次不出现目标字符集中任意字符的位置。 |
| find_last_not_of | 查找字符串中最后一次不出现目标字符集中任意字符的位置。 |
| substr | 从指定位置开始,截取指定长度的子串并返回。 |
| compare | 按字典序比较两个字符串,返回 0(相等)、正数(当前字符串更大)或负数(当前字符串更小)。 |
这里简单的记一下,这里有个比原网址还不错的网站:string - C++ Reference
string使用
注:这是c++98版本的string

在(3)里的len = npos,是已有缺省值,npos=-1(是str const里应该静态成员变量,在外使用得用string::npos。-1是取最大值),在我们不传入对应参数时会自动全部拷贝
一、构造
string()
cpp
int main()
{
string s1;//默认构造
string s2("hello");//带参构造
string s3(s2);//拷贝构造
string s4(s2, 2, 3);//子串构造,从第二个位置开始截取3个字符(如果截取字符大于已有字符,截取完直接结束)
string s5("World", 3);//从\0结尾的字符串取(也叫做C风格字符串)的前3个字符构造。
string s6(5, '*');//用5个'*'构造
string s7(s2.begin(), s2.begin() + 3);//通过迭代器范围构造(以s2为例,取从begin到begin+3的字符)
//支持流插入输出
cout << s1 << endl;
cout << s2 << endl;
cout << s3 << endl;
cin >> s1;
cout << s1 << endl;
return 0;
}
operatoe[]函数:

string重载了operatoe[],可以像数组一样直接修改任意的内容:
cpp
//逻辑演示重载了operatoe[],并非完整代码
class string
{
public:
char& operator[](size_t i)
{
return _str[i];
}
private:
char* _str;//tring底层大概就是这样,指针、存字符串、开空间
size_t _size;
size_t _capacity;
};
int main(){
string s2("hello");
string s2[0] = 'x';//直接修改指定位置
}
size函数
早期开发时,使用的是length,但发现有局限,在数里用length不合理,于是出现个size,比length更有通用性,常用。
size接口可以让我们获取字符长度:

cpp
void demo_string3()
{
string s2("hello world");
cout << s2.length() << endl;
cout << s2.size() << endl;
}
这里可以用于读取打印数组存储的字符串:
cpp
void demo_string1()
{
string s1;
string s2("hello world");
cout << s1 << s2 << endl;
s2[0] = 'x';
cout << s1 << s2 << endl;
for (size_t i = 0; i < s2.size(); i++)
{
cout << s2[i] << " ";//调用size打印
}
cout << endl;
}
int main(){
demo_string1();
}
迭代器string::iterator:
cpp
void demo_string1()
{
string s1;
string s2("hello world");
const string s3("hello world");
cout << s1 << s2 << endl;
s2[0] = 'x';
cout << s1 << s2 << endl;
string::iterator it = s2.begin();
while (it != s2.end())
{
//*it +=2;//iterator迭代器可以被修改,按照字母表往后推两个字母
cout << *it << " ";
++it;
}
string::const_iterator it = s3.begin();//const_iterator是实现const的迭代器,指向内容不能修改,但依然可以++it。begin()也会自动调用const版本,限制到只读范围
while (it != s3.end())
{
cout << *it << " ";
++it;
}
cout << endl;
}
int main(){
demo_string1();
}
string::iterator it = s2.begin();定义一个迭代器it( 根据编译器的不同底层实现可能是或不是指针),并让它指向s2的起始位置。
while里,it从s2的begin()(第一个字符)开始,直到遇到end()(最后一个字符)停止。
begin()自带const版本,会判断是普通、const迭代器,选用对应版本。const版本限制的是const int*而不是int* const。
const迭代器不是加了const的迭代器(conststring::const_iterator it = s3.begin();)。
iterator 不只支持数组,还提供了**统一的容器访问方式,**是个很重要的函数。
反向迭代器string::reverse_iterator:
cpp
void demo_string2()
{
string s2("hello world");
string::iterator it = s2.begin();
while (it != s2.end())
{
cout << *it << " ";
++it;
}
cout << endl;
//反向迭代器
string::reverse_iterator rit = s2.rbegin();//理解指向最后一个字符
while (rit != s2.rend())//rend可以理解指向第一个字符
{
cout << *rit << " ";
++rit;//reverse_iterator 倒着走,++就是往前走
}
cout << endl;
}
反向迭代器的逻辑是倒着从后往前遍历,所以使用的还是++rit。这里明确的一点是,反向迭代器不是原生的指针,而是一个封装的类,只有重载了运算符,才能让++倒着走。
这里rbegin()也通begin()一样,自带const版本。
auto与范围for
以下为C++十一提供的访问容器方式,叫做范围for
范围for,for循环后的括号由冒号:分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围。自动迭代,自动取数据,自动判断结束。
auto,自动推导。自动赋值迭代,虽然没用写明,但底层依然是迭代器原理。
范围for适合和auto搭配使用。
cpp
auto = &x//后面是啥类型auto是啥类型
auto* = &x //auto是指针
ayto& = x //auto是引用
cpp
for (auto ch : s2)
{
cout << ch << " ";
}
cout << endl;
但要注意,ch只是s2里面每个字符的拷贝,相当于是一个局部变量,在使用iterator一样的修改字符功能时,不会影响s2的原字符串。
auto不能空定义和定义数组。如auto e;这样,会导致编译器报错,还有auto不能做参数,但可以做返回值(谨慎使用,不太方便)
auto最大的作用就是用来简化代码;范围for适合用于数组和容器。
cpp
int main()
{
int array[] = { 1, 2, 3, 4, 5 };
// C++98的遍历
for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i)
{
array[i] *= 2;
}
for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i)
{
cout << array[i] << endl;
}
// C++11的遍历
for (auto& e : array)
e *= 2;
for (auto e : array)
cout << e << " " << endl;
string s1("hello world");
//string::const_iterator cit = s3.begin();
auto cit = s3.begin();
const string s2("hello world");
//string::const_iterator cit = s3.rbegin();
auto rcit = s3.begin();
}
太多了就不一一写出来,具体功能可以去看看上面了成员函数了解和网站
typeid
typeid可以帮助我们看类型:
cpp
int main()
{
int a = 10;
auto b = a;
auto c = 'a';
auto d = func1();
cout << typeid(b).name() << endl;
cout << typeid(c).name() << endl;
cout << typeid(d).name() << endl;
}
capacity
capacity可以查看当前分配空间大小,当size增长到超过当前分配空间时(大于等于16),容器会自动重新分配更大的内存。
空间扩容机制:string开的前空间填满后,会自动扩容。直接看的话结论是第一次二倍扩容,第二次以后一点五倍扩容,当然不同编译器也会有区别,一般都认为是两倍扩容。
这里我的编译器出现二倍扩容是因为第一次扩容时有一个空间(容量固定为16)转换到另一个空间(可扩容空间,扩容为固定空间的两倍,接后续扩容以原1.5倍扩容)来特殊处理存储。或者当存入的size大于原有的空间,也会直接开一个新的空间存放。
capacity返回的数组是不包含\0的,返回的值是默认减1。如实际16的空间返回值是15。
cpp
void TestPushBack()
{
string s;
size_t sz = s.capacity();
cout << "making s grow:\n";
for (int i = 0; i < 100; ++i)
{
s.push_back('c');
if (sz != s.capacity())//capacity变化时打印新的容量
{
sz = s.capacity();
cout << "capacity changed: " << sz << '\n';
}
}
}
reserve()
频繁扩容要不断开空间,是一件不好的事情,所以妩媚可以提前预留空间以减少扩容次数。
预留、固定开多少空间,如reserve(100),是开辟100大小的空间,但实际上编译器开辟空间会做一个字节对齐,开辟的空间肯定会比要求开辟的空间大一些。
同样的,因为不计算\0,所以我们不能直接开辟对等大小的空间,要多大一点预留位置。
reserve可以提前开空间,提高效率,避免反复扩容。
reserve还可以发起一个不具有约束力的请求缩小容量。
reserve (size_t n = 0);
n大于capacity开辟的值当前空间会主动扩容。
n小于原有size的值,最多缩小到与size一样的空间大小。
n在capacity与size之间时,不确定了,不同编译器的做法不一样,我这的VS编译器是选着不缩保留已经开辟的空间 。
operator[a]与at(a)
读取第a个位置的字符,也有const的版本
at和operator[a]与at(a)[]功能一样,但越界是会抛出异常,poerator[]则会断言。
cpp
string s = "hello";
char ch = s[1];
s[2] = 'X';
char ch = s.at(1);
s.at(2) = 'X';
其余简单介绍:
stoi:字符串转整形,to_string将整形转化为字符串int。
to_string:其他类型转换成字符串
insert:在指定位置插入字符 / 字符串的成员函数。
swap:可以交换两个string对象的内容
cpp
string s1 = "hello";
string s2 = "world";
s1.swap(s2);
swap(s1, s2);
// 交换s1和s2的内容
// 交换后:s1="world",s2="hello"