目录
[1. STL简介](#1. STL简介)
[1.1 什么是STL](#1.1 什么是STL)
[1.2 STL的发展](#1.2 STL的发展)
[1.3 STL的六大组件](#1.3 STL的六大组件)
[1.4 STL的重要性](#1.4 STL的重要性)
[1.5 如何学习STL](#1.5 如何学习STL)
[2. string类](#2. string类)
[2.1 string类的概念](#2.1 string类的概念)
[2.2 string类与STL的关系:](#2.2 string类与STL的关系:)
[2.3 为什么要学习string类](#2.3 为什么要学习string类)
[3. 标准库中的string类](#3. 标准库中的string类)
[3.1 string类](#3.1 string类)
[3.2 基本语法](#3.2 基本语法)
[3.3 访问和修改字符](#3.3 访问和修改字符)
[1. 下标访问( [] 运算符)](#1. 下标访问( [] 运算符))
[2. 迭代器访问](#2. 迭代器访问)
[3. 范围for循环(C++11及以上)](#3. 范围for循环(C++11及以上))
[4. 结合 auto 的遍历](#4. 结合 auto 的遍历)
[4. 总结:](#4. 总结:)
1. STL简介
1.1 什么是STL
STL(Standard Template Library , 标准模板库)是C++标准库的重要组成部分 , 它基于模板技术 , 提供了一系列通用的数据结构(容器)和算法 , 旨在提高代码的复用性 , 效率和标准化。
1.2 STL的发展
- 起源 : 20世纪80年代末 , 由Alexander Stepanov(亚历山大·斯特潘诺夫)等人提出 , 最初并非C++标准 , 后因实用性被纳入C++标准。
- 标准化 : 1998年 , STL正式成为C++98标准的一部分 , 后续在C++11 , C++14等版本中不断扩展(如增加无序容器 , 移动语义支持等)。
- 目标 : 通过模板实现"泛型编程" , 让数据结构和算法脱离具体数据类型 , 实现跨类型复用。
- 原始版本: Alexander Stepanov、Meng Lee 在惠普实验室完成的原始版本 , 本着开源精神 , 他们声明允许任何人任意运用 , 拷贝 , 修改 , 传播 , 商业使用这些代码 , 无需付费。唯一的条件就是也需要向原始版本一样做开源使用。HP版本 - 所有STL实现版本的始祖。
- P.J.版本 : 由P.J. Plauger开发 , 继承自HP版本 , 被Windows Visual C++采用 , 不能公开或修改 , 缺陷 : 可读性比较低 , 符号命名比较怪异。
- RW版本 : 由Rouge Wage公司开发 , 继承自HP版本 , 被C++ Builder 采用 , 不能公开或修改 , 可读性一般。
- SGI版本: 由Silicon Graphics Computer Systems, Inc公司开发 , 继承自HP版本。被GCC(Linux)采用 , 可移植性好 , 可公开 , 修改甚至贩卖 , 从命名风格和编程风格上看 , 阅读性非常高。我们后面学习STL要阅读部分源代码 , 主要参考的就是这个版本。
1.3 STL的六大组件

1.4 STL的重要性
- 提高开发效率 : 无需重复实现常见数据结构(如链表、哈希表)和算法(如排序) , 直接调用STL组件 , 减少代码量。
- 保证性能 : STL由专家优化 , 底层实现高效(如 vector 的连续内存 , map 的红黑树平衡操作)。
- 标准化 : 统一接口让代码更易读 , 易维护 , 不同开发者可基于STL达成共识。
- 泛型编程典范 : 展示了模板技术的强大 , 为C++后续发展奠定基础。
1.5 如何学习STL
- 基础入门
- 先掌握C++模板语法(类模板 , 函数模板) , 理解STL的实现基础。
- 逐个学习核心容器 : vector (最常用)→ list → map / set → 无序容器 , 掌握其初始化 , 增删改查 , 迭代器使用。
- 算法实践
- 熟悉 <algorithm> 头文件中的常用算法 : sort , find , count , reverse 等 , 结合容器练习。
- 理解算法的时间复杂度(如sort 是O(n log n) , find 是O(n))。
- 深入原理
- 了解容器底层实现(如 vector 动态扩容机制 , map 红黑树结构) , 解释为何某些操作高效/低效。
- 学习迭代器失效场景(如 vector 插入元素可能导致迭代器失效)。
- 实战应用
- 在编程题 , 项目中主动使用STL , 例如用 unordered_map 做哈希映射 , vector 存储动态数组。
- 对比不同容器的适用场景(如需要随机访问用 vector , 频繁插入删除用 list)。
2. string类
2.1 string类的概念
简单说 , std::string 是标准库中独立的字符串工具 , 但因适配 STL 的迭代器和算法 , 常被视为"准 STL 组件" , 在实际开发中与 STL 容器(如 vector<string> )配合频繁。
2.2 string类与STL的关系:
std::string 不属于 STL 的核心组成部分 , 但它与 STL 关系密切 , 且常被一起使用。
具体来说:
- 归属上 : std::string 是 C++ 标准库中专门用于处理字符串的类 , 定义在 <string> 头文件中 , 其设计早于 STL 标准化 , 核心目标是封装字符串操作(如拼接、比较、长度计算等) , 并非 STL 最初定义的"容器、算法、迭代器"体系的核心成员。
- 关联上 : std::string 支持 STL 的迭代器(可通过 begin() / end() 获取) , 能直接使用 STL 算法(如 std::sort 对字符串字符排序 , std::find 查找字符) , 且其内部实现也依赖模板技术 , 与 STL 的泛型思想一致。
2.3 为什么要学习string类
- C语言中 , 字符串是以 \0 结尾的一些字符的集合 , 为了操作方便 , C标准库中提供了一些str系列的库函数 , 但是这些库函数与字符串是分离开的 , 不太符合OOP的思想 , 而且底层空间需要用户自己管理 , 稍不留神可能还会越界访问。
- 在OJ中 , 有关字符串的题目基本以string类的形式出现 , 而且在常规工作中 , 为了简单、方便、快捷 , 基本都使用string类 , 很少有人去使用C库中的字符串操作函数。
3. 标准库中的string类
3.1 string类
std::string 是 C++ 标准库中用于处理字符串的类 , 定义在 <string> 头文件中 , 属于 std 命名空间。使用 string 类时 , 需包含头文件 <string> , 并通常通过 using namespace std; (或指定 std::string) 来使用。它封装了字符串的存储与操作 , 无需像 C 语言字符数组那样手动管理内存(如扩容 , 越界等问题)。
3.2 基本语法
1. 包含头文件与命名空间
要使用 string 类 , 需包含头文件 <string> , 且通常使用 std 命名空间(也可通过 std::string 显式指定):
cpp#include <string> using namespace std; // 或使用 std::string
2. 字符串的初始化
- 默认初始化:创建空字符串。
cppstring str1;
- 用字符串字面量初始化:
cppstring str2 = "hello"; string str3("world");
- 用部分字符初始化 : string(const char* s, size_t n) , 从字符串 s 的开头取 n 个字符初始化。
cppstring str4("abcdef", 3); // str4 为 "abc"
- 用重复字符初始化: string(size_t n, char c) , 创建包含 n 个字符 c 的字符串。
cppstring str5(5, 'a'); // str5 为 "aaaaa"
- 拷贝初始化 : 用另一个 string 对象初始化。
cppstring str6 = str2; // str6 为 "hello"
3. 字符串的基本操作
(1) 长度与容量
- size() / length() : 获取字符串长度(字符个数) , 二者功能一致。
cppstring str = "hello"; cout << str.size() << endl; // 输出 5 cout << str.length() << endl; // 输出 5
- capacity() :获取字符串当前已分配的内存能容纳的字符数(不包含结尾 \0 )。
cppcout << str.capacity() << endl;
- empty() :判断字符串是否为空 , 空返回 true , 否则返回 false。
cppif (str.empty()) { cout << "字符串为空" << endl; } else { cout << "字符串不为空" << endl; }
- resize(size_t n, char c = '\0') :调整字符串长度为 n , 若 n 大于原长度 , 新增字符用 c 填充 , 若 n 小于原长度 , 截断字符串。
cppstr.resize(8, 'x'); // str 变为 "helloxxx" str.resize(3); // str 变为 "hel"
(2) 访问字符
- 下标访问( [] ):通过索引访问字符 , 类似数组 , 索引从 0 开始。
cppstring str = "hello"; cout << str[0] << endl; // 输出 'h' str[1] = 'E'; // str 变为 "hEllo"
- 迭代器访问:string 支持迭代器 , 可用于遍历等操作。
cppfor (string::iterator it = str.begin(); it != str.end(); ++it) { cout << *it << " "; }
- 也可结合 auto 简化
cppfor (auto it = str.begin(); it != str.end(); ++it) { cout << *it << " "; }
- 范围 for 循环(C++11+) : 更简洁地遍历每个字符。
cppfor (char c : str) { cout << c << " "; }
(3) 字符串拼接
- + 运算符:用于字符串与字符串 , 字符串与字符的拼接。
cppstring str1 = "hello"; string str2 = " world"; string str3 = str1 + str2; // str3 为 "hello world" string str4 = str1 + '!'; // str4 为 "hello!"
- += 运算符:在原字符串后追加内容。
cppstring str = "hello"; str += " world"; // str 为 "hello world" str += '!'; // str 为 "hello world!"
- append() 方法:功能类似 += , 也可追加部分字符。
cppstr.append("test"); // 追加字符串 "test" str.append("abcdef", 3); // 追加 "abcdef" 的前 3 个字符 "abc"
(4) 字符串查找
- find(const string& str, size_t pos = 0) : 从位置 pos 开始查找子串 str , 返回第一次出现的起始索引 , 若未找到返回 string::npos (一个很大的无符号整数)。
cppstring str = "hello world hello"; size_t pos = str.find("hello"); // 找到,返回 0 pos = str.find("hello", 1); // 从索引 1 开始找,返回 12 if (str.find("test") == string::npos) { cout << "未找到子串" << endl; }
- rfind(const string& str, size_t pos = npos): 从位置 pos 开始反向查找子串 str ,回最后一次出现的起始索引。
cppsize_t pos = str.rfind("hello"); // 返回 12
- find_first_of(const string& str, size_t pos = 0): 从位置 pos 开始 , 查找 str 中任意一个字符第一次出现的索引。
cppstring str = "hello world"; size_t pos = str.find_first_of("aeiou"); // 找元音字母,返回 1('e' 的索引)
- find_last_of(const string& str, size_t pos = npos):从位置 pos 开始 , 查找 str 中任意一个字符最后一次出现的索引。
cppsize_t pos = str.find_last_of("aeiou"); // 返回 7('o' 的索引)
(5) 字符串截取与替换
- substr(size_t pos = 0, size_t len = npos):从位置 pos 开始 , 截取长度为 len 的子串 , 若 len 为 npos 则截取到字符串末尾。
cppstring str = "hello world"; string sub = str.substr(6, 5); // sub 为 "world" sub = str.substr(0, 5); // sub 为 "hello" sub = str.substr(6); // sub 为 "world"
- replace(size_t pos, size_t len, const string& str):从位置 pos 开始 , 替换长度为 len 的子串为 str。
cppstring str = "hello world"; str.replace(6, 5, "C++"); // str 变为 "hello C++"
(6) 字符串比较
- 关系运算符( == 、 != 、 < 、 > 、 <= 、 >= ):按字典序比较两个字符串。
cppstring str1 = "abc"; string str2 = "abd"; if (str1 < str2) { cout << "str1 小于 str2" << endl; }
- compare(const string& str) :比较当前字符串与 str , 返回值:
小于 0:当前字符串小于 str;
等于 0:两者相等;
大于 0:当前字符串大于 str。
cppint res = str1.compare(str2); if (res < 0) { cout << "str1 < str2" << endl; }
(7) 字符串的插入与删除
- insert(size_t pos, const string& str):在位置 pos 插入字符串 str。
cppstring str = "hello"; str.insert(5, " world"); // str 变为 "hello world"
- erase(size_t pos = 0, size_t len = npos) :从位置 pos 开始 , 删除长度为 len 的子串 , 若 len 为 npos 则删除到字符串末尾。
cppstring str = "hello world"; str.erase(5, 6); // 删除从索引 5 开始的 6 个字符,str 变为 "hello" str.erase(2); // 删除从索引 2 开始到末尾的字符,str 变为 "he"
4. 其他常用操作
- 清空字符串: clear() , 将字符串变为空。
cppstring str = "hello"; str.clear();
- 交换字符串: swap(string& str) , 交换两个字符串的内容。
cppstring str1 = "hello"; string str2 = "world"; str1.swap(str2); // str1 变为 "world",str2 变为 "hello"
掌握这些 string 类的语法和操作 , 能满足大部分字符串处理的需求 , 让 C++ 中的字符串操作更加高效和简洁。
3.3 访问和修改字符
在 C++ 中 , 访问 std::string 字符主要有下标访问、迭代器访问、范围 for 循环、结合 auto 的遍历这几种方式 , 下面分别详细讲解:
1. 下标访问( [] 运算符)
std::string 重载了 [] 运算符,支持像数组一样通过索引访问单个字符,索引从 0 开始,到 size() - 1 结束。
语法形式:
cppchar& string::operator[](size_t pos); // 非 const 版本,可修改字符 const char& string::operator[](size_t pos) const; // const 版本,仅读取
特点与使用场景:
- 优点 : 语法简洁 , 像操作数组一样直观 , 访问效率高(直接通过索引定位内存)。
- 缺点:
- 不做范围检查 , 若 pos 超出 [0, size()-1] , 会导致未定义行为(如程序崩溃、乱码等)。
- 只能访问单个字符 , 遍历所有字符时需手动控制索引。
示例:
cpp#include <string> #include <iostream> using namespace std; int main() { string str = "hello"; // 访问单个字符 cout << str[0] << endl; // 输出 'h' // 修改字符 str[1] = 'E'; cout << str << endl; // 输出 "hEllo" // 遍历所有字符(需手动控制索引) for (size_t i = 0; i < str.size(); ++i) { cout << str[i] << " "; } // 输出:h E l l o return 0; }
2. 迭代器访问
迭代器是 STL 中连接容器和算法的"桥梁" , std::string 提供了迭代器来遍历字符 , 支持多种迭代器类型(如正向迭代器、反向迭代器、const 迭代器等)。
迭代器类型:
cpp- string::iterator :正向迭代器,支持读写操作。 - string::const_iterator :const 正向迭代器,仅支持读操作(用于 const string )。 - string::reverse_iterator :反向迭代器,从后往前遍历。 - string::const_reverse_iterator :const 反向迭代器。
常用迭代器方法
cpp- begin()/cbegin() :返回指向字符串首字符的迭代器(cbegin() 返回 const_iterator )。 - end()/cend() :返回指向字符串**末尾(最后一个字符的下一个位置)**的迭代器。 - rbegin()/crbegin() :返回指向字符串最后一个字符的反向迭代器。 - rend()/crend() :返回指向字符串首字符前一个位置的反向迭代器。
特点与使用场景:
- 优点:
- 通用:符合 STL 迭代器规范 , 可与 STL 算法(如 std::sort , std::find )无缝配合。
- 安全:遍历逻辑由迭代器封装 , 不易因索引错误导致越界。
- 灵活:支持正向、反向等多种遍历方式。
- 缺点:语法比下标访问复杂 , 新手可能需要适应。
示例
cpp#include <string> #include <iostream> using namespace std; int main() { string str = "hello"; // 正向迭代器(读写) for (string::iterator it = str.begin(); it != str.end(); ++it) { *it = toupper(*it); // 转大写 } cout << str << endl; // 输出 "HELLO" // const 正向迭代器(只读,用于 const string) const string constStr = "world"; for (string::const_iterator cit = constStr.cbegin(); cit != constStr.cend(); ++cit) { cout << *cit << " "; } // 输出:w o r l d // 反向迭代器 for (string::reverse_iterator rit = str.rbegin(); rit != str.rend(); ++rit) { cout << *rit << " "; } // 输出:O L L E H return 0; }
3. 范围for循环(C++11及以上)
范围 for 是 C++11 引入的语法糖 , 专门用于遍历容器(如 std::string , STL 容器)或数组的所有元素,语法非常简洁。
语法形式:
cppfor (元素类型 变量名 : 容器/数组) { // 操作变量名 }
特点与使用场景:
- 优点:
- 语法极简:无需手动控制索引或迭代器, 直接遍历所有元素。
- 安全:由编译器保证遍历范围的正确性 , 不会越界。
- 缺点:
- 仅支持顺序遍历 , 无法灵活控制遍历方向(如反向)。
- 若需要修改字符 , 需注意是否用引用(否则是值拷贝 , 修改不影响原字符串)。
示例:
cpp#include <string> #include <iostream> using namespace std; int main() { string str = "hello"; // 只读遍历(值拷贝) for (char c : str) { cout << c << " "; } // 输出:h e l l o // 读写遍历(引用) for (char& c : str) { c = toupper(c); // 转大写,修改原字符串 } cout << str << endl; // 输出 "HELLO" // const 字符串的只读遍历 const string constStr = "world"; for (char c : constStr) { cout << c << " "; } // 输出:w o r l d return 0; }
4. 结合 auto 的遍历
auto 是 C++11 引入的类型推导关键字 , 可让编译器自动推导变量类型。结合迭代器或范围 for 时 , 能进一步简化代码。
与迭代器结合 : 利用 auto 推导迭代器类型 , 避免写冗长的 string::iterator 等类型名。
示例:
cpp#include <string> #include <iostream> using namespace std; int main() { string str = "hello"; // 正向迭代器(auto 推导类型) for (auto it = str.begin(); it != str.end(); ++it) { *it = toupper(*it); } cout << str << endl; // 输出 "HELLO" // 反向迭代器(auto 推导类型) for (auto rit = str.rbegin(); rit != str.rend(); ++rit) { cout << *rit << " "; } // 输出:O L L E H return 0; }
与范围 for 结合 : 用 auto 推导范围 for 中元素的类型,进一步简化代码。
示例:
cpp#include <string> #include <iostream> using namespace std; int main() { string str = "hello"; // 只读遍历(auto 推导为 char) for (auto c : str) { cout << c << " "; } // 输出:h e l l o // 读写遍历(auto& 推导为 char&) for (auto& c : str) { c = toupper(c); } cout << str << endl; // 输出 "HELLO" // const 字符串的只读遍历(auto 推导为 char) const string constStr = "world"; for (auto c : constStr) { cout << c << " "; } // 输出:w o r l d return 0; }
总结:四种方式的对比与选择
实际开发中 , 建议优先使用范围 for + auto(简洁且安全);若需复杂遍历(如反向、与 STL 算法配合) , 则用迭代器 + auto;仅需随机访问单个字符时 , 再考虑下标访问。
4. 总结:
STL(标准模板库)是C++标准库的重要组成部分 , 提供通用数据结构和算法 , 基于模板技术实现泛型编程。STL包含六大组件, , 如容器、算法、迭代器等 , 能提高开发效率、保证性能并促进代码标准化。string类虽不属于STL核心 , 但常与STL配合使用 , 提供强大的字符串操作功能 , 包括初始化、访问、修改、查找、拼接等。学习STL和string类需掌握模板语法、核心容器使用、算法实践及底层原理 , 建议通过实际项目应用加深理解。
感谢大家的观看!