2.22 STL 中string的学习
1.什么是STL
STL standard template libarary 标准模板库:是C++标准库的重要组成部分 不仅是一个可复用的组件库 而且是一个包罗数据结构和算法的软件框架
2.STL六大组件
容器 算法 迭代器 仿函数 适配器 分配器
3.STL三大境界
学习 熟练 扩展
string的学习
通俗理解
string就是一个会自己长大的字符数组 自带了很多工具函数
为什么使用string
自动内存管理 方便操作 安全(不会越界) 与C兼容
C++ string 类常用接口说明
1. 简介
string 是 C++ 标准库中的字符串类,封装了动态字符数组,自动管理内存,提供丰富的成员函数。使用 string 比 C 风格字符串更安全、方便。
2. 头文件与命名空间
#include<string>usingnamespace std;// 或 std::string3. 常用接口分类
3.1 构造与赋值
| 接口 | 说明 | 示例 |
|---|---|---|
string() | 默认构造空字符串 | string s; |
string(const char* s) | 用 C 字符串构造 | string s("hello"); |
string(size_t n, char c) | 构造 n 个字符 c | string s(5, 'a'); → “aaaaa” |
string(const string& str) | 拷贝构造 | string s2(s1); |
string(const string& str, size_t pos, size_t len = npos) | 子串构造 | string s(s1, 2, 3); |
operator= | 赋值 | s = "new"; 或 s = s2; |
assign() | 多种赋值方式 | s.assign(5, 'x'); → “xxxxx” |
3.2 容量操作
| 接口 | 说明 | 示例 |
|---|---|---|
size() / length() | 返回字符个数 | int n = s.size(); |
capacity() | 返回当前分配的内存容量 | size_t cap = s.capacity(); |
empty() | 判断是否为空 | if (s.empty()) |
resize(size_t n) | 改变大小(截断或填充默认字符) | s.resize(10); |
resize(size_t n, char c) | 改变大小并用 c 填充新位置 | s.resize(10, '!'); |
reserve(size_t n = 0) | 预分配至少 n 个字符的空间 | s.reserve(100); |
clear() | 清空字符串 | s.clear(); |
3.3 元素访问
| 接口 | 说明 | 示例 |
|---|---|---|
operator[](size_t pos) | 下标访问(不检查越界) | char ch = s[2]; |
at(size_t pos) | 带越界检查的访问(越界抛异常) | char ch = s.at(2); |
front() | 返回第一个字符 | char f = s.front(); |
back() | 返回最后一个字符 | char b = s.back(); |
3.4 修改操作
| 接口 | 说明 | 示例 |
|---|---|---|
operator+= | 尾部追加 | s += " world";s += '!'; |
append() | 追加多种形式 | s.append("xxx");s.append(3, '!'); |
push_back(char c) | 尾部添加一个字符 | s.push_back('?'); |
insert(size_t pos, const string& str) | 在 pos 前插入 | s.insert(5, "xx"); |
erase(size_t pos = 0, size_t len = npos) | 删除从 pos 开始的 len 个字符 | s.erase(5, 3);s.erase(); 清空 |
replace(size_t pos, size_t len, const string& str) | 替换子串 | s.replace(2, 3, "abc"); |
swap(string& other) | 交换两个字符串 | s1.swap(s2); |
3.5 查找操作
所有查找函数返回 size_t,未找到返回 string::npos。
| 接口 | 说明 | 示例 |
|---|---|---|
find(const string& str, size_t pos = 0) | 从 pos 开始正向查找子串 | size_t p = s.find("lo"); |
find(char c, size_t pos = 0) | 从 pos 开始查找字符 | p = s.find('o', 5); |
rfind() | 反向查找 | p = s.rfind("lo"); |
find_first_of(const string& str, size_t pos = 0) | 查找 str 中任意字符首次出现的位置 | p = s.find_first_of("aeiou"); |
find_last_of() | 查找最后一个出现的位置 | p = s.find_last_of("aeiou"); |
find_first_not_of() | 查找第一个不属于 str 的字符 | p = s.find_first_not_of("abc"); |
find_last_not_of() | 查找最后一个不属于 str 的字符 |
3.6 子串与比较
| 接口 | 说明 | 示例 |
|---|---|---|
substr(size_t pos = 0, size_t len = npos) | 提取子串 | string sub = s.substr(2, 3); |
compare(const string& str) | 比较(返回 0 相等,<0 小于,>0 大于) | int res = s1.compare(s2); |
compare(size_t pos, size_t len, const string& str) | 比较子串 | s1.compare(2, 3, s2); |
| 关系运算符 | ==!=<><=>= | if (s1 == s2) |
3.7 迭代器支持
| 接口 | 说明 | 示例 |
|---|---|---|
begin() / end() | 返回正向迭代器 | for (auto it = s.begin(); it != s.end(); ++it) |
rbegin() / rend() | 返回反向迭代器 | 逆向遍历 |
| 范围 for | 基于范围的循环 | for (char c : s) |
3.8 与 C 风格字符串转换
| 接口 | 说明 | 示例 |
|---|---|---|
c_str() | 返回 const char* 指针(以 ‘\0’ 结尾) | printf("%s", s.c_str()); |
data() | C++11 起同 c_str() | const char* p = s.data(); |
从 const char* 构造 | 隐式转换(若构造函数非 explicit) | string s = "hello"; |
4. 注意事项
string::npos:find等函数未找到时返回,值为-1(实际是size_t最大值)。- 越界访问:
operator[]不检查越界,at()会检查并抛异常。 - 迭代器失效:插入、删除、重新分配可能导致迭代器失效。
c_str()结果有效性:在 string 被修改或销毁后,返回的指针可能失效。- 内存管理:
string通常有小字符串优化(SSO),短字符串不分配堆内存。
5. 综合示例
#include<iostream>#include<string>usingnamespace std;intmain(){ string s ="C++ string"; s.append(" is powerful");// 查找并替换 size_t pos = s.find("powerful");if(pos != string::npos){ s.replace(pos,8,"awesome");}// 遍历for(char c : s){ cout << c;} cout << endl;// 转 C 字符串输出printf("C-string: %s\n", s.c_str());// 子串 string sub = s.substr(4,6);// "string" cout <<"substr: "<< sub << endl;return0;}以上是 string 最常用的接口,掌握这些足以应对大多数日常编程需求。
resize(size_t n)
改变大小(截断或者填充默认字符)
resize(size_t n,char c)
用指定字符填充扩展的区域或者截断
reserve(size_t n = 0)
预分配至少n个字符的空间
###clear()清空字符串
iterator的使用
iterator是一个迭代器 可以在不同的容器中进行移动 为所有的容器和算法提供一个接口 是通用的
借助begin函数(返回容器的第一个元素地址)和end函数(容器最后一个元素地址的下一个地址)我们可以进行遍历
代码实现
list<int> lt ={1,2,3,4,5}; list<int>::iterator lit = lt.begin();while(lit != lt.end()){ std::cout <<*lit << std::endl;++lit;}auto(范围for)遍历
auto看上去高级 实际上也是iterator 只是自动调用了 写起来比较简单
代码如下:
list<int> l2 ={1,2,3,4,5};for(auto ch : l2){ std::cout << ch <<" ";} std::cout << std::endl;嗯 严格意义上来说一行就能遍历加上打印
如果想要改变容器里面的值 需要加上引用 不加引用实际上是传值调用 不会影响容器里面的值
auto关键字
编译器自动查找替换 不能单独定义 必须要编译器推导
当类型名过长的时候可以简化代码 但是会降低可读性
比如:
std::map<std::string,std::string>::iterator it = dict.begin();这一大坨就可以被替换为
auto it = dict.begin();auto不能定义数组 不能做参数 但是可以做返回值
反向迭代器 reverse_iterator
大部分都和正向迭代器iterator一样 但是要使用rbegin(指向最后一个字符)和rend(指向第一个字符的前一个位置)
list<int> lt ={1,2,3,4,5};//list<int>::reverse_iterator lit = lt.rbegin();auto lit = lt.rebegin();while(lit != lt.rend()){ std::cout <<*lit << std::endl;++lit;}这里也是反向移动的
反正这些东西发明出来就是为了方便遍历 那个代码少用哪个 谁爽用谁
list<int> lt ={1,2,3,4,5};for(auto it = v.rebegin();it != v.rend();++it){ cout <<*it <<" ";}return0;