目录
0. 引言
STL(standard template libaray-标准模板库)是C++的一个重要组成部分,不仅是一个可复用的组件库,而且是一个包罗数据结构与算法的软件框架。因此,学习和使用STL显得格外的重要。
在C语言中字符串是以'\0'结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列的库函数, 但是这些库函数与字符串是分离开的,不太符合OOP的思想,而且底层空间需要用户自己管理,稍不留神可能还会越界访问。于是 C++ string类,它可以看做是一个管理字符串的数据结构。
1. string 类
1.1 string类的基本概念
string 就是字符串的意思,是 C++用来代替char数组的数据结构。里面封装了一些常用的方法,方便我们地对其进行一些操作,而且string的空间大小是动态变化的,大大减小了不必要的花销 。
1.2 string类与char*的区别
(1) char* 是一个指针
(2) string 本质上是一个类,类的内部封装了char*,即 string 是一个 char* 型的容器
(3) string 管理所分配的 char* 内存,不用担心复制越界和取值越界等
C++ string 类它提供了一系列成员函数和操作符,使得字符串的操作更加方便和灵活。
1.3 string类的作用
string类 它可以存储任意数目的字符。它可以用来表示文本或其他字符串数据,例如:文本文件内容、网络传输的数据、数据库中的文本列、用户输入或输出等。
string 类的作用有以下几个方面:string类可以存储和操作任意数目的字符,可以处理文本数据,如搜索、替换、截取子串等操作。使用string类可以方便地进行字符串操作。 如连接(concatenate)两个字符串、删除(erase)字符串中的一些字符、复制(copy)字符串等。string类是C++标准库的一部分,可用于在不同的计算机和操作系统之间进行可靠的代码交换。
2. string 的接口使用
2.1 string 类对象的默认成员函数
函数名称 | 功能 |
constructor | 构造函数 |
destructor | 析构函数 |
operator= | 赋值重载 |
2.1.1 构造函数 - 初始化
红色框出来的是重点,下面的代码我们做出了一些说明:
string(); // 构造一个空字符串
string (const char* s); // 用C-string来构造string类对象
string (const char* s, size_t n); // 用C-string的前n个字符来构造string类对象
string (size_t n, char c); // 生成n个c字符的字符串
string (const string& str); // 利用原先的字符串做拷贝构造
// 拷贝str字符串中从pos位置开始的len个字符
string (const string& str, size_t pos, size_t len = npos);
照着说明,我们可以做出如下的验证:
我们看到了这句代码: string (const string& str, size_t pos, size_t len = npos);
其中为什么结尾给 npos 呢?npos 代表什么,我们接下来看一下。
2.1.2 npos 含义
Maximum value for size_t 说明了其为无符号整数的最大值,我们将其打印发现:
这是一个非常大的值,编译器默认就是从当前的这个【pos】位置开始一直到字符串末尾,这就是npos的作用。 也就是说 npos则直接到达字符串的末尾。
2.2 赋值重载 - 初始化
下面的代码是我们做出了一些说明:
string& operator= (const string& str); // 将一个string对象赋值给到另一个
string& operator= (const char* s); // 将一个字符串赋值给到string对象
string& operator= (char c); // 将一个字符赋值给到string对象
照着说明,我们同样可以做出如下的验证:
2.3 析构函数
2.2 string 类对象的访问和遍历操作
接口 | 说明 |
operator[ ] | 返回pos位置的字符,const string类对象调用 |
at() | 返回pos位置的字符,const string类对象调用 |
迭代器 | begin() end() / rbegin() rend() |
范围for | 范围for遍历 |
2.2.1 operator[ ]
看到 [ ] 我们肯定能够想到运算符重载。operator [ ] 具有两个重载函数,一个是普通对象,一个则是const对象。
因此我们可以根据 operator[ ] 就可以利用 下标+ [ ] 来访问string中任何一个元素了。例如:
我们知道,sting 类的对象中的字符是存储在堆上面的,那是否和C语言相同以'\0'结尾呢?我们调试来看:string封装很多,不过仍然能看到结尾的 '\0' 。
同时我们也可以利用 下标+ [ ] 对字符来进行更改:
因此,opterator [ ] 可以让我们对字符串的使用类似于数组的增删查改。
2.2.2 at
我们可以看到 at 同样具有两个重载函数,一个是普通对象,一个则是const对象。
2.2.3 迭代器
接口 | 说明 |
begin() | 返回指向第一个元素的迭代器 |
end() | 返回指向最后一个元素的下一个位置的迭代器 |
rbegin() | 返回指向最后一个元素的反向迭代器 |
rend() | 返回指向第一个元素的前一个位置的反向迭代器 |
迭代器是是另一种访问string中内容的方式,图示如下,我们使用一个 it 去保存这个字符串begin处的位置,那么在其不断进行后移的过程中,就是在遍历这个字符串,当其到达最后的 end 处时,也就遍历完了。反向迭代器则方向相反,过程如图所示:
参考代码如下:
string s1("hello world");
cout << s1 << endl;
cout << "正向迭代器:";
string::iterator it = s1.begin();
while (it != s1.end())
{
cout << *it << " ";
it++;
}
cout << endl;
cout << "反向迭代器:";
string::reverse_iterator rit = s1.rbegin();
while (rit != s1.rend())
{
cout << *rit << " ";
rit++;
}
cout << endl;
迭代器的这种方式,其实和指针非常得类似,其中 iterator 是像指针但不是指针。
2.2.4 范围 for
范围 for 就是我们上次提到过的 语法糖。
需要注意的是,范围for的底层实现还是【迭代器】,它在遍历的时候相当于是将 *it
的数据给到当前的ch,和迭代器的本质还是一样的。
2.3 string 常见的容量操作
接口 | 说明 |
size | 返回字符串有效字符长度 |
length | 返回字符串有效字符长度 |
capacity | 返回空间总大小 |
empty | 检测字符串是否为空串,是返回true,否返回false |
reverse | 为字符串预留空间 |
resize | 将有效字符串的个数改成 n 个,多出的空间用字符c填充 |
2.3.1 size 和 length![](https://i-blog.csdnimg.cn/blog_migrate/c7947800fbb22c62d09c3f553c840f16.png)
![](https://i-blog.csdnimg.cn/blog_migrate/98d24f6b1f1db59edb4f3944f2ec12c0.png)
我们发现size 和 length 定义是一模一样的,都是返回字符串的长度。 结果也是一模一样的。
2.3.2 capacity
capacity() 表示为字符串开辟的空间大小。
下面我们来看一下 windows 平台下是如何动态扩容的:
string s;
size_t sz = s.capacity();
cout << "making s grow:\n";
for (int i = 0; i < 100; ++i)
{
s.push_back('L');
if (sz != s.capacity())
{
sz = s.capacity();
cout << "capacity changed: " << sz << '\n';
}
}
Windows下的VS中,扩容以1.5倍大小扩容。
2.3.3 empty ![](https://i-blog.csdnimg.cn/blog_migrate/e64b2063d58434515039e4f96dd5fce0.png)
检测字符串是否为空串,是返回true,否返回false 。
2.3.4 reserve ![](https://i-blog.csdnimg.cn/blog_migrate/0722ed82cadc4ba199228d98f3730282.png)
reserve 可以提前为字符串开辟一个大小为 n 的 空间。
因此 reserve 可以为我们提前开好所需的空间。
2.3.4 resize ![](https://i-blog.csdnimg.cn/blog_migrate/d524e1c9825a392d164c33c30c2a0c1f.png)
上面说到 reserve 只是扩容,而 resize 是初始化字符串的个数。 另外使用resize时,size和capacity都会改变,并且二者的capacity 比给出的参数 n 还会大一点。
因此当 n > 当前的字符串长度,resize 在末尾插入所需数量的字符以达到n的大小来扩展当前内容
那么,当 n < 当前的字符串长度会发生什么呢?
n < 当前的字符串长度时,则会去进行一个删除的操作,删除第n个字符之后的字符 。
2.4 string 常见修改操作
接口 | 说明 |
push_back | 在字符串后尾插字符 |
append | 在字符串后面追加一个字符串 |
operator+= | 在字符产后面追加字符串str |
insert | 在指定位置插入字符或字符串 |
assert | 使用指定的字符串替换原字符串 |
erase | 删除字符串中的一部分 |
replace | 替换指定区间的字符串 |
pop_back | 删除字符串的最后一个字符串 |
swap | 收缩到合适大小 |
这里我们对 operator+=() , insert(), erase 进行详细解释。
2.4.1 operator +=
+= 是运算符重载,一共有三个重载形式,分别是拼接一个string类的对象、一个字符串、一个字符 。
可以看出+=十分方便,可以代替push_back, append 的使用。
2.4.2 insert
insert 函数重载形式很多:
说明如下:
// 在指定位置插入一个string对象
string& insert (size_t pos, const string& str);
// 在指定位置插入一个string对象里的一部分
string& insert (size_t pos, const string& str, size_t subpos, size_t sublen);
// 在指定位置插入一个字符串
string& insert (size_t pos, const char* s);
// 在指定位置插入一个字符串的前n个字符
string& insert (size_t pos, const char* s, size_t n);
// 在指定位置插入n个字符
string& insert (size_t pos, size_t n, char c);
// 在指定迭代器的位置插入n个字符
void insert (iterator p, size_t n, char c);
// 在指定迭代器的位置插入一个字符,并且返回一个迭代器的位置
iterator insert (iterator p, char c);
例如我们可以做出如下的使用:
2.4.3 erase ![](https://i-blog.csdnimg.cn/blog_migrate/d6cafd72f0d33c5d0d62c76f16bb1789.png)
例如:
2.5 string其他操作
此外还有一些其他比较重要的操作,在这里我们将其详细的列出来:
接口 | 说明 |
c_str | 返回 C语言格式字符串 |
substr | 在str中从pos位置开始往,截取n个字符,然后将其返回 |
find | 从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置 |
rfind | 从字符串pos位置开始往前找字符c,返回该字符在字符串中的位置 |
find_frist_of | 从前往后找第一个匹配的字符 |
find_last_of | 从后往前找第一个匹配的字符 |
find_first_not_of | 从前往后找第一个不匹配的字符 |
find_last_not_of | 从后往前找第一个不匹配的字符 |
operator+() | 尽量少用,因为传值返回,导致深拷贝效率低 |
relational operator | 大小比较 |
operator>>() | 流插入重载 |
operator<<() | 流提取重载 |
getline | 获取一行字符串 |