[C.C++] C++ STL标准模板库详解

314 0
Honkers 2026-5-7 15:06:15 来自手机 | 显示全部楼层 |阅读模式

C++ STL标准模板库详解

简介

STL(Standard Template Library,标准模板库)是C++标准库的重要组成部分,它提供了一套通用的、可复用的组件,包括容器、迭代器、算法、函数对象、适配器和空间配置器。本文重点讲解最常用的三大组件——容器、迭代器和算法,并通过丰富的代码示例帮助开发者掌握STL的核心用法。

一、STL 总览

1.1 六大组件

组件说明头文件示例
容器(Container)存储和组织数据的数据结构, ,
迭代器(Iterator)连接容器和算法的桥梁各容器头文件
算法(Algorithm)操作容器内容的模板函数
仿函数(Functor)重载了 operator() 的类
适配器(Adapter)修改容器或函数接口的包装器,
空间配置器(Allocator)负责内存分配与释放

1.2 容器分类

  1. STL 容器
  2. ├── 序列容器(Sequential)
  3. │ ├── vector 向量(动态数组)
  4. │ ├── deque 双端队列
  5. │ └── list 双向链表
  6. ├── 关联容器(Associative)
  7. │ ├── set 集合(唯一键)
  8. │ ├── multiset 多重集合
  9. │ ├── map 映射(键值对)
  10. │ └── multimap 多重映射
  11. └── 容器适配器(Adapter)
  12. ├── stack 栈(LIFO)
  13. ├── queue 队列(FIFO)
  14. └── priority_queue 优先队列
复制代码

[图片占位符:STL容器分类结构图]

二、vector(向量)

2.1 简介

vector 是一种动态数组,能自动管理内存,支持随机访问。在尾部插入和删除元素的时间复杂度为 O(1),在中间或头部操作为 O(n)。

  1. #include <vector>
  2. using namespace std;
复制代码

2.2 构造方式

  1. vector<int> vec1; // 默认构造,空向量
  2. vector<int> vec2(10); // 10 个元素,值为 0
  3. vector<int> vec3(10, 5); // 10 个元素,值为 5
  4. vector<int> vec4(vec3); // 拷贝构造
  5. vector<int> vec5(vec3.begin(), vec3.end()); // 区间构造
  6. int arr[] = {1, 2, 3, 4, 5};
  7. vector<int> vec6(arr, arr + 5); // 从数组构造
复制代码

2.3 元素访问

  1. vector<int> vec = {10, 20, 30, 40, 50};
  2. vec[0]; // 下标访问(不检查越界)
  3. vec.at(2); // at 访问(越界抛出异常)
  4. vec.front(); // 第一个元素
  5. vec.back(); // 最后一个元素
  6. vec.data(); // 返回底层数组指针
复制代码

2.4 添加与删除

  1. vector<int> vec;
  2. // 添加元素
  3. vec.push_back(10); // 尾部添加
  4. vec.emplace_back(20); // 尾部原地构造(C++11,更高效)
  5. vec.insert(vec.begin(), 5); // 在指定位置插入
  6. vec.insert(vec.begin() + 2, 3, 100); // 插入 3 个 100
  7. // 删除元素
  8. vec.pop_back(); // 删除末尾元素
  9. vec.erase(vec.begin()); // 删除指定位置
  10. vec.erase(vec.begin(), vec.begin()+2);// 删除区间
  11. vec.clear(); // 清空
  12. // 重新赋值
  13. vec.assign(7, 100); // 7 个 100
  14. vec.assign(vec2.begin(), vec2.end()); // 用另一个容器赋值
复制代码

2.5 容量与大小

  1. vector<int> vec = {1, 2, 3};
  2. vec.size(); // 元素个数:3
  3. vec.empty(); // 是否为空:false
  4. vec.capacity(); // 当前容量(>= size)
  5. vec.reserve(100); // 预分配容量(避免多次重新分配)
  6. vec.resize(10); // 改变大小(多出的元素默认初始化)
  7. vec.shrink_to_fit(); // 释放多余容量(C++11)
复制代码

2.6 完整使用示例

  1. #include <iostream>
  2. #include <vector>
  3. #include <cstring>
  4. using namespace std;
  5. int main() {
  6. int arr[] = {1, 2, 3, 4, 5};
  7. const char *str = "Hello STL";
  8. vector<int> vec_i(arr, arr + 5);
  9. vector<char> vec_c(str, str + strlen(str));
  10. // 使用迭代器遍历
  11. for (vector<int>::iterator it = vec_i.begin(); it != vec_i.end(); ++it) {
  12. cout << *it << " ";
  13. }
  14. cout << endl;
  15. // 输出:1 2 3 4 5
  16. // 使用下标遍历
  17. for (size_t i = 0; i < vec_c.size(); i++) {
  18. cout << vec_c[i];
  19. }
  20. cout << endl;
  21. // 输出:Hello STL
  22. return 0;
  23. }
复制代码

2.7 vector 的注意事项

  • 不支持前插:vector 没有提供 push_front,因为在头部插入需要移动所有元素
  • 迭代器失效:插入或删除元素可能导致迭代器失效,需要重新获取
  • reserve 优化:如果知道大致元素数量,提前 reserve 可以避免多次内存重分配

三、deque(双端队列)

3.1 简介

deque(Double-Ended Queue)与 vector 类似,支持随机访问,但额外支持在头部高效插入和删除。

  1. #include <deque>
  2. using namespace std;
复制代码

3.2 与 vector 的区别

特性vectordeque
头部插入不支持支持 push_front
内存分布连续内存分段连续
capacity/reserve支持不支持
随机访问O(1)O(1)

3.3 使用示例

  1. #include <iostream>
  2. #include <deque>
  3. using namespace std;
  4. void print_deque(deque<int> que, const char *name) {
  5. cout << "========= " << name << endl;
  6. for (auto it = que.begin(); it != que.end(); ++it) {
  7. cout << *it << " ";
  8. }
  9. cout << endl;
  10. }
  11. int main() {
  12. deque<int> que(5, 0); // 5 个 0
  13. print_deque(que, "initQue");
  14. // 输出:0 0 0 0 0
  15. que.push_back(12);
  16. que.push_back(13);
  17. print_deque(que, "push_back");
  18. // 输出:0 0 0 0 0 12 13
  19. que.push_front(1);
  20. que.push_front(2);
  21. print_deque(que, "push_front");
  22. // 输出:2 1 0 0 0 0 0 12 13
  23. que.pop_back();
  24. que.pop_back();
  25. print_deque(que, "pop_back");
  26. // 输出:2 1 0 0 0 0 0
  27. que.pop_front();
  28. que.pop_front();
  29. print_deque(que, "pop_front");
  30. // 输出:0 0 0 0 0
  31. return 0;
  32. }
复制代码

四、list(双向链表)

4.1 简介

list 是由节点组成的双向链表,不支持随机访问(没有 operator[]),但在任意位置插入和删除元素的时间复杂度都是 O(1)。

  1. #include <list>
  2. using namespace std;
复制代码

4.2 与 vector/deque 的区别

特性vectordequelist
随机访问O(1)O(1)不支持
头部插入不支持O(1)O(1)
中间插入/删除O(n)O(n)O(1)
内存开销高(每个节点额外指针)

4.3 list 特有操作

  1. list<int> lst1 = {123, 0, 34, 1123};
  2. list<int> lst2 = {12, 100};
  3. lst1.sort(); // 排序
  4. lst2.sort(); // 排序
  5. // lst1: 0, 34, 123, 1123
  6. // lst2: 12, 100
  7. lst1.merge(lst2); // 合并两个有序链表
  8. // lst1: 0, 12, 34, 100, 123, 1123
  9. // lst2: 空(元素被转移到 lst1)
  10. lst1.reverse(); // 反转
  11. lst1.unique(); // 去除连续重复元素
  12. lst1.splice(it, lst2); // 将 lst2 的元素拼接到 lst1 的指定位置
复制代码

4.4 使用示例

  1. #include <iostream>
  2. #include <list>
  3. using namespace std;
  4. void PrintIt(list<int> n) {
  5. for (list<int>::iterator iter = n.begin(); iter != n.end(); ++iter)
  6. cout << *iter << " ";
  7. }
  8. int main() {
  9. list<int> listn1, listn2;
  10. listn1.push_back(123);
  11. listn1.push_back(0);
  12. listn1.push_back(34);
  13. listn1.push_back(1123);
  14. listn2.push_back(100);
  15. listn2.push_back(12);
  16. listn1.sort();
  17. listn2.sort();
  18. PrintIt(listn1); // 0 34 123 1123
  19. cout << endl;
  20. PrintIt(listn2); // 12 100
  21. listn1.merge(listn2);
  22. cout << endl;
  23. PrintIt(listn1); // 0 12 34 100 123 1123
  24. return 0;
  25. }
复制代码

五、set / multiset(集合)

5.1 set 简介

set 是有序的唯一元素集合,底层实现为红黑树。元素既充当数据又充当关键字,最大的特点是元素唯一自动排序

  1. #include <set>
  2. using namespace std;
复制代码

5.2 基本用法

  1. set<int, less<int>> myset;
  2. myset.insert(30);
  3. myset.insert(10);
  4. myset.insert(20);
  5. myset.insert(10); // 插入失败,元素已存在
  6. // 遍历(自动排序)
  7. for (auto it = myset.begin(); it != myset.end(); ++it) {
  8. cout << *it << " "; // 输出:10 20 30
  9. }
  10. // 查找
  11. auto it = myset.find(20);
  12. if (it != myset.end()) {
  13. cout << "找到:" << *it << endl;
  14. }
  15. // 删除
  16. myset.erase(10);
  17. myset.erase(myset.begin());
  18. // 大小
  19. myset.size();
  20. myset.empty();
  21. // count:存在返回 1,不存在返回 0
  22. myset.count(20);
复制代码

5.3 multiset

multiset 与 set 的区别在于允许重复元素:

  1. multiset<int, less<int>> myset;
  2. myset.insert(10);
  3. myset.insert(10); // 成功,允许重复
  4. myset.count(10); // 返回 2
复制代码

六、map / multimap(映射)

6.1 map 简介

map 存储键值对(key-value),通过唯一的 key 快速查找 value。底层实现为红黑树,key 自动排序。与 set 不同,map 的 key 和 value 是分开的。

  1. #include <map>
  2. using namespace std;
复制代码

6.2 基本用法

  1. map<char, int, less<char>> mymap;
  2. // 插入
  3. mymap.insert(pair<char, int>('a', 100));
  4. mymap.insert(map<char, int>::value_type('b', 200));
  5. mymap['c'] = 300; // 使用下标操作符
  6. // 遍历
  7. for (auto it = mymap.begin(); it != mymap.end(); ++it) {
  8. cout << it->first << " => " << it->second << endl;
  9. }
  10. // a => 100
  11. // b => 200
  12. // c => 300
  13. // 查找
  14. auto it = mymap.find('b');
  15. if (it != mymap.end()) {
  16. cout << "找到:" << it->second << endl;
  17. }
  18. // 下标访问
  19. mymap['a']; // 返回 100
  20. mymap['d']; // 如果不存在,创建并初始化为 0
复制代码

6.3 map 与 set 的关键区别

特性setmap
元素数据即关键字key-value 分离
下标运算不支持支持 []
访问方式*itit->first, it->second

6.4 multimap

multimap 允许 key 重复,不支持 [] 运算符

  1. multimap<int, string> mmap;
  2. mmap.insert(pair<int, string>(1, "hello"));
  3. mmap.insert(pair<int, string>(1, "world")); // 允许重复 key
  4. mmap.insert(pair<int, string>(2, "foo"));
  5. // 查找某个 key 的所有值
  6. auto range = mmap.equal_range(1);
  7. for (auto it = range.first; it != range.second; ++it) {
  8. cout << it->second << endl;
  9. }
复制代码

6.5 注意事项

  • set 和 map 的 key 只能存在一个,insert 重复值会失败
  • multiset 和 multimap 允许重复 key
  • 关联容器的 find() 成员函数比泛型 find() 算法效率更高(O(log n) vs O(n))

七、容器适配器

7.1 stack(栈)

后进先出(LIFO):

  1. #include <stack>
  2. stack<int> stk;
  3. stk.push(10);
  4. stk.push(20);
  5. stk.push(30);
  6. stk.top(); // 30
  7. stk.pop(); // 移除 30
  8. stk.size(); // 2
  9. stk.empty(); // false
复制代码

7.2 queue(队列)

先进先出(FIFO):

  1. #include <queue>
  2. queue<int> q;
  3. q.push(10);
  4. q.push(20);
  5. q.push(30);
  6. q.front(); // 10
  7. q.back(); // 30
  8. q.pop(); // 移除 10
  9. q.size(); // 2
复制代码

7.3 priority_queue(优先队列)

元素按优先级自动排序(默认大顶堆):

  1. #include <queue>
  2. priority_queue<int> pq;
  3. pq.push(30);
  4. pq.push(10);
  5. pq.push(20);
  6. pq.top(); // 30(最大值)
  7. pq.pop(); // 移除 30
  8. // 自定义小顶堆
  9. priority_queue<int, vector<int>, greater<int>> min_pq;
复制代码

八、迭代器

8.1 迭代器的作用

迭代器是连接容器和算法的桥梁,类似于指向容器元素的指针。可以递增迭代器使其依次指向容器中的每个元素。

8.2 迭代器类型

类型能力支持的容器
输入迭代器只读,单向istream
输出迭代器只写,单向ostream
前向迭代器读写,单向forward_list
双向迭代器读写,双向list, set, map
随机访问迭代器读写,随机vector, deque

8.3 迭代器基本操作

  1. vector<int> vec = {1, 2, 3, 4, 5};
  2. // 获取迭代器
  3. vector<int>::iterator it = vec.begin(); // 首元素
  4. vector<int>::iterator end = vec.end(); // 尾后元素(不包含)
  5. // C++11 自动类型推导
  6. auto it2 = vec.begin();
  7. // const 迭代器(只读)
  8. vector<int>::const_iterator cit = vec.cbegin();
  9. // 遍历
  10. for (auto it = vec.begin(); it != vec.end(); ++it) {
  11. cout << *it << " ";
  12. }
  13. // 范围 for 循环(C++11)
  14. for (auto& elem : vec) {
  15. cout << elem << " ";
  16. }
复制代码

8.4 begin/end 与 cbegin/cend

  1. // begin() 和 end() 返回的类型根据容器是否为 const 而定
  2. // cbegin() 和 cend()(C++11)始终返回 const_iterator
  3. auto it = vec.cbegin(); // 不论 vec 是否为 const,返回只读迭代器
复制代码

8.5 迭代器失效问题

以下操作可能导致迭代器失效:

  1. vector<int> vec = {1, 2, 3, 4, 5};
  2. auto it = vec.begin();
  3. vec.push_back(6); // 可能导致 it 失效(重新分配内存)
  4. vec.insert(vec.begin() + 1, 10); // it 之后的位置可能失效
  5. vec.erase(vec.begin()); // it 及之后的迭代器失效
  6. // 安全做法:操作后重新获取迭代器
  7. it = vec.begin();
复制代码

九、常用算法

9.1 非修改序列算法

  1. #include <algorithm>
  2. vector<int> vec = {1, 2, 3, 4, 5};
  3. // 查找
  4. auto it = find(vec.begin(), vec.end(), 3);
  5. // 计数
  6. int n = count(vec.begin(), vec.end(), 3);
  7. // 遍历
  8. for_each(vec.begin(), vec.end(), [](int x) {
  9. cout << x << " ";
  10. });
复制代码

9.2 修改序列算法

  1. vector<int> vec1 = {1, 2, 3};
  2. vector<int> vec2(3);
  3. // 复制
  4. copy(vec1.begin(), vec1.end(), vec2.begin());
  5. // 填充
  6. fill(vec2.begin(), vec2.end(), 0);
  7. // 变换
  8. transform(vec1.begin(), vec1.end(), vec2.begin(),
  9. [](int x) { return x * 2; });
  10. // 替换
  11. replace(vec1.begin(), vec1.end(), 2, 20);
  12. // 移除(逻辑删除,不改变大小)
  13. auto new_end = remove(vec1.begin(), vec1.end(), 20);
  14. vec1.erase(new_end, vec1.end()); // 物理删除
  15. // 反转
  16. reverse(vec1.begin(), vec1.end());
复制代码

9.3 排序算法

  1. vector<int> vec = {5, 2, 8, 1, 9};
  2. // 排序(默认升序)
  3. sort(vec.begin(), vec.end());
  4. // 自定义排序
  5. sort(vec.begin(), vec.end(), greater<int>()); // 降序
  6. sort(vec.begin(), vec.end(), [](int a, int b) {
  7. return a > b;
  8. });
  9. // 稳定排序
  10. stable_sort(vec.begin(), vec.end());
  11. // 部分排序
  12. partial_sort(vec.begin(), vec.begin() + 3, vec.end());
  13. // 检查是否有序
  14. is_sorted(vec.begin(), vec.end());
复制代码

9.4 查找算法(有序区间)

  1. vector<int> vec = {1, 2, 3, 4, 5, 6, 7, 8, 9};
  2. // 二分查找
  3. bool found = binary_search(vec.begin(), vec.end(), 5);
  4. // 下界/上界
  5. auto lower = lower_bound(vec.begin(), vec.end(), 5); // 第一个 >= 5
  6. auto upper = upper_bound(vec.begin(), vec.end(), 5); // 第一个 > 5
复制代码

9.5 数值算法

  1. #include <numeric>
  2. vector<int> vec = {1, 2, 3, 4, 5};
  3. // 累加
  4. int sum = accumulate(vec.begin(), vec.end(), 0);
  5. // 内积
  6. int product = inner_product(vec.begin(), vec.end(), vec.begin(), 0);
  7. // 部分和
  8. vector<int> result(5);
  9. partial_sum(vec.begin(), vec.end(), result.begin());
  10. // result: 1, 3, 6, 10, 15
复制代码

十、forward_list(前向链表,C++11)

  1. #include <forward_list>
  2. forward_list<int> fl = {1, 2, 3, 4};
  3. // 单向链表,迭代器只能 ++
  4. // 不支持 size() 操作
  5. // 提供 before_begin() 获取头节点前位置
  6. fl.push_front(0);
  7. fl.insert_after(fl.before_begin(), -1);
  8. for (auto it = fl.begin(); it != fl.end(); ++it) {
  9. cout << *it << " ";
  10. }
复制代码

十一、容器选择指南

需求场景推荐容器原因
随机访问,尾部增删vector连续内存,缓存友好
头尾增删deque支持两端高效操作
频繁中间插入/删除list链表结构,O(1) 操作
查找唯一元素set / unordered_set红黑树/哈希表
键值对查找map / unordered_map红黑树/哈希表
LIFO 操作stack适配器封装
FIFO 操作queue适配器封装
带优先级的队列priority_queue堆结构

十二、string 容器

严格来说,string 不属于STL容器,但它支持与容器相似的操作:

  1. #include <string>
  2. using namespace std;
  3. string s1 = "Hello";
  4. string s2 = "World";
  5. s1 + s2; // 拼接
  6. s1.size(); // 长度
  7. s1.substr(0, 3); // 子串
  8. s1.find("ell"); // 查找
  9. s1[0]; // 下标访问
  10. s1.empty(); // 是否为空
  11. // C++11 数字与字符串转换
  12. int n = stoi("123");
  13. string str = to_string(456);
复制代码

十三、C++/C 混合调用注意事项

在 C++ 中调用 C 编写的库时,必须使用 extern "C" 包裹头文件,否则由于 C++ 的名称修饰机制,链接器无法找到正确的符号。

  1. extern "C" {
  2. #include "c_library.h"
  3. }
复制代码

这个规则适用于所有 C 编写的静态库和动态库。

总结

STL 是 C++ 开发者必须掌握的核心工具库。合理使用 STL 容器和算法,不仅能提高开发效率,还能保证代码的质量和性能。

关键原则:

  • 根据使用场景选择合适的容器(随机访问选 vector,频繁插入选 list,查找选 map/set)
  • 理解迭代器的种类和失效机制,避免使用失效的迭代器
  • 善用泛型算法,减少手写循环
  • 优先使用 emplace 系列函数减少不必要的对象拷贝
  • 注意 STL 的线程安全性:读操作是线程安全的,写操作需要加锁

原始笔记来源: frasight/C++笔记.cpp(STL 部分)

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

中国红客联盟公众号

联系站长QQ:5520533

admin@chnhonker.com
Copyright © 2001-2026 Discuz Team. Powered by Discuz! X3.5 ( 粤ICP备13060014号 )|天天打卡 本站已运行