目录
前言
一、STL 六大组件回顾
二、vector 容器详解
三、迭代器分类
四、list 容器详解
五、map 容器详解
六、总结
前言
STL 是 C++ 开发的核心利器,几乎所有项目都会用到容器、迭代器与算法。相比于 C 语言手动实现顺序表、链表、哈希结构,STL 提供了一套标准化、高复用、高效率的通用组件,大幅提升开发效率。
本文结合实战代码、底层原理、避坑细节,完整讲解 STL 六大核心组成,重点剖析vector、list、map、multimap常用容器用法、迭代器分类、仿函数特性,适合新手学习、面试复盘。
一、STL 六大组件回顾
STL 标准组成分为六大模块,相互配合实现泛型编程:
容器:存储数据(vector、list、map、set 等)
迭代器:容器通用“泛型指针”,统一遍历方式
算法:排序、查找、去重等通用算法
仿函数:重载()的类/结构体,可像函数一样调用
配接器:基于容器改造(栈、队列、优先队列)
配置器:负责内存分配,vector 内存池底层依赖它
不同平台 STL :Linux 使用 SGI STL,Visual Studio 使用 PJ STL。
二、vector 容器详解
vector是动态数组,类似 C 语言的顺序表,支持随机访问。
2.1 vector 的三种访问方式
ar[i]可以表示三种情况:数组名、指针、对象名(重载了operator[])。
int main() { std::vector<int> ar = {12, 23, 34, 45, 56, 67, 78, 89, 90, 11}; // 1. front() / back():首尾元素 cout << ar.front() << endl; // 第一个元素 cout << ar.back() << endl; // 最后一个元素 // 2. at():带边界检查 for (int i = 0; i < ar.size(); i++) { cout << ar.at(i) << endl; } // 3. operator[]:不带边界检查 for (int i = 0; i < ar.size(); i++) { cout << ar[i] << endl; ar.operator[](i); // 等价写法 } }2.2 at() vs operator[] 的区别
| 方法 | 越界行为 | 返回类型 | 适用场景 |
|---|---|---|---|
ar.at(i) | 一定会检查越界,越界抛出std::out_of_range异常 | 引用 | 需要安全访问时 |
ar[i] | 不检查越界,行为未定义 | 引用 | 确定不越界时(性能更高) |
不同平台表现
| 平台 | at(i) | ar[i](越界时) |
|---|---|---|
| Windows + VS(Debug) | 抛异常 | 断言中断(检查越界) |
| Windows + VS(Release) | 抛异常 | 不检查,未定义行为 |
| Linux + g++ | 抛异常 | 不检查,可能崩溃或乱值 |
关键点:VS 在 Debug 模式下
operator[]也会检查越界(断言),但 Release 模式下不检查。不能依赖 VS Debug 的行为写代码,因为换到 Linux 或其他编译器就没有这个保护了。
2.3 范围 for 循环
int main() { std::vector<int> ar = {12, 23, 34, 45, 56, 67, 78, 89, 90, 100}; // 值拷贝(效率低,类类型时尤其明显) for (auto x : ar) { printf("%5d", x); } // 引用(推荐,不拷贝) for (auto& x : ar) { printf("%5d", x); } // const 引用(只读) for (const auto& x : ar) { printf("%5d", x); } }
auto&可以少一次拷贝,推荐使用。
2.4 迭代器遍历
int main() { std::vector<int> ar = {12, 23, 34, 45, 56, 67, 78, 89, 90, 100}; std::vector<int>::iterator it = ar.begin(); std::vector<int>::iterator end = ar.end(); for (; it != end; ++it) { cout << *it << endl; } // const 迭代器:只读 std::vector<int>::const_iterator cit; }2.5 reserve() vs resize()
reserve(n):预留空间,只改变capacity,不改变size。
std::vector<int> ar; ar.reserve(10); cout << ar.size(); // 0 cout << ar.capacity(); // 10resize(n):设置容器大小,同时改变size和capacity。
std::vector<int> ar; ar.resize(10); cout << ar.size(); // 10 cout << ar.capacity(); // 至少 10(可能更大)核心区别:
reserve:只扩容,不创建元素。size不变。resize:扩容并创建元素。size变为n。
2.6 vector 常用函数
int main() { std::vector<int> ar; ar.resize(10000); cout << "size:" << ar.size() << endl; // 10000 cout << "capacity:" << ar.capacity() << endl; // 至少 10000 // 删除区间 [begin+2, end) ar.erase(ar.begin() + 2, ar.end()); cout << "size:" << ar.size() << endl; // 2 cout << "capacity:" << ar.capacity() << endl; // 容量不变 // 收缩容量,使 capacity == size ar.shrink_to_fit(); cout << "size:" << ar.size() << endl; // 2 cout << "capacity:" << ar.capacity() << endl; // 2 }2.7 vector 类型别名
template<class T> class vector { public: using value_type = T; using reference = T&; using const_reference = const T&; using pointer = T*; using const_pointer = const T*; };三、迭代器分类
| 迭代器类型 | 特点 | 支持的操作 | 容器示例 |
|---|---|---|---|
| 随机迭代器 | 支持任意跳跃 | +、-、+=、-=、[] | vector、deque |
| 双向迭代器 | 一次走一步,可前可后 | ++、-- | list、map、set |
| 正向迭代器 | 只能单向向后 | ++ | hash_map、hash_set |
随机迭代器示例
int main() { std::vector<int> ivec = {12, 23, 34, 45, 56}; std::vector<int>::iterator it = ivec.begin(); cout << *it << endl; // 12 cout << *(it + 3) << endl; // 45 it += 3; cout << *it << endl; // 45 it -= 2; cout << *it << endl; // 34 }双向迭代器示例
int main() { std::list<int> ilist = {12, 23, 34, 45, 56}; std::list<int>::iterator it = ilist.begin(); it++; // 可以向前 ++it; it--; // 可以向后 }四、list 容器详解
4.1 list 访问方式
int main() { std::list<int> ilist = {12, 23, 34, 45, 56}; // 1. 范围 for for (auto& x : ilist) { cout << x << " "; } // 2. 迭代器 for (auto it = ilist.begin(); it != ilist.end(); ++it) { cout << *it << " "; } // 3. front/back cout << ilist.front() << endl; // 第一个元素 cout << ilist.back() << endl; // 最后一个元素 }4.2 list 插入
int main() { std::list<int> mylist = {1, 3, 5, 7, 9}; std::list<int> helist = {2, 4, 6, 8, 10}; // 在开头插入 10 个 23 mylist.insert(mylist.begin(), 10, 23); // 在中间位置插入 auto it = mylist.begin(); ++it; ++it; ++it; mylist.insert(it, helist.begin(), helist.end()); }4.3 list 拼接(splice)
int main() { std::list<int> mylist = {1, 3, 5, 7, 9}; std::list<int> helist = {2, 4, 6, 8, 10}; auto it = mylist.begin(); mylist.splice(it, helist, helist.begin(), helist.end()); // helist 中的元素被移动到 mylist 中 }4.4 list 删除
// remove:按值删除 mylist.remove(45); // remove_if:按条件删除 int x; cin >> x; mylist.remove_if([x](const int& y) { return y > x; });4.5 list 注意事项
| 操作 | 说明 |
|---|---|
clear() | 删除所有元素,size归 0,表头节点保留 |
| 头插/尾插 | 都是 O(1) 效率 |
erase | 按迭代器删除,不能按值删除 |
erase(end) | ❌ 不能删除 end,会崩溃 |
// 删除区间 [begin, end) mylist.erase(mylist.begin(), mylist.end());五、map 容器详解
map是键值对容器(key-value),像字典,一个唯一键对应一个值。
5.1 map 基础操作
int main() { std::map<int, std::string> ismap; // key 不可重复 std::multimap<int, std::string> mismap; // key 可以重复 }5.2 std::pair 与 make_pair
int main() { // 构造 pair std::pair<int, std::string> p1(12, "apple"); std::pair<int, std::string> p2 = std::make_pair(12, "banana"); // 访问成员 cout << p1.first << " " << p1.second << endl; // C++17 结构化绑定 auto [id, name] = p1; cout << id << " " << name << endl; // pair 比较:先比 first,再比 second cout << (p1 > p2) << endl; }5.3 给 map 插入元素
int main() { std::map<int, std::string> ismap = { {12, "zhangsan"}, {23, "lisi"}, {34, "wangwu"}, {45, "zhaoliu"} }; // 四种插入方式 ismap.insert({100, "beijing"}); ismap.insert(std::pair<int, std::string>(78, "henan")); ismap.insert(std::make_pair(56, "guangzhou")); ismap.insert(std::map<int, std::string>::value_type(67, "yunnan")); // 遍历 for (auto& xp : ismap) { cout << xp.first << "=>" << xp.second << endl; } }5.4 map 的 operator[] 行为
int main() { std::map<int, std::string> ismap = { {12, "zhangsan"}, {23, "lisi"}, {34, "wangwu"} }; int key; while (cin >> key, key != -1) { cout << ismap[key] << endl; // 行为: // 1. 去 map 里查找 key // 2. 如果有,返回值的引用 // 3. 如果没有,自动插入这个 key! } }注意:
operator[]会在 key 不存在时自动插入,慎用。
5.5 map 实现计数器
int main() { const int n = 10000; std::vector<int> ivec; ivec.reserve(n); srand(time(nullptr)); for (int i = 0; i < n; ++i) { ivec.push_back(rand() % 100); } // 统计数字出现次数 map<int, int> countMap; for (auto& num : ivec) { countMap[num]++; // 核心!一行代码完成统计 } for (auto& pair : countMap) { cout << pair.first << " " << pair.second << endl; } }5.6 删除 map 元素
// 方式1:通过迭代器删除 auto it = ismap.find("lisi"); if (it != ismap.end()) { ismap.erase(it); } // 方式2:通过 key 删除 ismap.erase("zhaoliu");5.7 map 合并(merge)
int main() { std::map<int, char> icmap1 = {{1,'a'}, {3,'b'}, {5,'c'}}; std::map<int, char> icmap2 = {{2,'d'}, {4,'e'}, {3,'f'}, {6,'g'}}; icmap1.merge(icmap2); // 冲突的 key 不会被合并 }5.8 lower_bound / upper_bound
| 函数 | 作用 |
|---|---|
lower_bound(key) | 返回第一个≥ key的元素 |
upper_bound(key) | 返回第一个> key的元素 |
auto it = icmap.lower_bound(key); // 第一个 >= key auto it = icmap.upper_bound(key); // 第一个 > key5.9 equal_range
auto it = icmap.equal_range(key); // 返回一对迭代器 [first, second) // first = lower_bound(key) // second = upper_bound(key)5.10 multimap 用法
int main() { std::multimap<int, char> icmap; for (int i = 0; i < 26; ++i) { icmap.insert({rand() % 10, i + 'a'}); } // 获取所有 key 为特定值的元素 auto it = icmap.equal_range(key); for (auto i = it.first; i != it.second; ++i) { cout << i->first << " " << i->second << endl; } }5.11 按频度排序
// 按数字出现次数升序打印 std::multimap<int, int> iimap; for (auto& pair : countMap) { iimap.insert(make_pair(pair.second, pair.first)); } PrintMap(iimap); // 按次数从小到大输出六、总结
| 知识点 | 核心要点 |
|---|---|
| STL 六大组件 | 容器、迭代器、算法、仿函数、配接器、配置器 |
| vector vs list | vector 连续存储,支持随机访问;list 链表,适合频繁插入删除 |
| at() vs operator[] | at()抛异常(安全),operator[]不检查边界(高效) |
| reserve vs resize | reserve只扩容量(size 不变),resize同时扩容量和大小 |
| shrink_to_fit | 收缩容量使capacity == size |
| 迭代器类型 | 随机迭代器(vector)、双向迭代器(list/map)、正向迭代器(hash) |
| map 特点 | 键值对存储,key 唯一且自动排序,operator[]会插入不存在的 key |
| multimap | key 可重复,无operator[] |
| pair | 存储两个值的结构体,first和second成员 |
| 结构化绑定(C++17) | auto [a, b] = pair一键拆包 |
| lower_bound / upper_bound | lower_bound找第一个 ≥ key,upper_bound找第一个 > key |
| equal_range | 同时返回 lower_bound 和 upper_bound |
| map 实现计数器 | countMap[num]++一行代码完成统计 |
| splice | list 专属,O(1) 拼接两个链表 |
| remove / remove_if | list 按值/按条件删除元素 |