news 2026/5/25 3:06:42

C++ STL 容器详解:vector、list、map 与迭代器完全指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C++ STL 容器详解:vector、list、map 与迭代器完全指南

目录

前言

一、STL 六大组件回顾

二、vector 容器详解

三、迭代器分类

四、list 容器详解

五、map 容器详解

六、总结


前言

STL 是 C++ 开发的核心利器,几乎所有项目都会用到容器、迭代器与算法。相比于 C 语言手动实现顺序表、链表、哈希结构,STL 提供了一套标准化、高复用、高效率的通用组件,大幅提升开发效率。

本文结合实战代码、底层原理、避坑细节,完整讲解 STL 六大核心组成,重点剖析vector、list、map、multimap常用容器用法、迭代器分类、仿函数特性,适合新手学习、面试复盘。


一、STL 六大组件回顾

STL 标准组成分为六大模块,相互配合实现泛型编程:

  1. 容器:存储数据(vector、list、map、set 等)

  2. 迭代器:容器通用“泛型指针”,统一遍历方式

  3. 算法:排序、查找、去重等通用算法

  4. 仿函数:重载()的类/结构体,可像函数一样调用

  5. 配接器:基于容器改造(栈、队列、优先队列)

  6. 配置器:负责内存分配,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(); // 10

resize(n):设置容器大小,同时改变sizecapacity

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*; };

三、迭代器分类

迭代器类型特点支持的操作容器示例
随机迭代器支持任意跳跃+-+=-=[]vectordeque
双向迭代器一次走一步,可前可后++--listmapset
正向迭代器只能单向向后++hash_maphash_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); // 第一个 > key

5.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 listvector 连续存储,支持随机访问;list 链表,适合频繁插入删除
at() vs operator[]at()抛异常(安全),operator[]不检查边界(高效)
reserve vs resizereserve只扩容量(size 不变),resize同时扩容量和大小
shrink_to_fit收缩容量使capacity == size
迭代器类型随机迭代器(vector)、双向迭代器(list/map)、正向迭代器(hash)
map 特点键值对存储,key 唯一且自动排序,operator[]会插入不存在的 key
multimapkey 可重复,无operator[]
pair存储两个值的结构体,firstsecond成员
结构化绑定(C++17)auto [a, b] = pair一键拆包
lower_bound / upper_boundlower_bound找第一个 ≥ key,upper_bound找第一个 > key
equal_range同时返回 lower_bound 和 upper_bound
map 实现计数器countMap[num]++一行代码完成统计
splicelist 专属,O(1) 拼接两个链表
remove / remove_iflist 按值/按条件删除元素
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/25 3:05:59

昇腾CANN ge 仓的图优化 Pass:哪些 Pass 真正影响推理性能

前言 你训练好一个模型&#xff0c;导出 ONNX&#xff0c;转成 CANN 的 OM 模型。推理时发现&#xff1a;延迟 89ms&#xff0c;吞吐只有 1200 samples/s。 你开始调优&#xff1a; 加 --op_precision_modeforce_fp16 → 延迟 72ms&#xff08;快 19%&#xff09;加 --auto_tun…

作者头像 李华
网站建设 2026/5/25 2:57:20

昇腾NPU强化学习训练实战——从PPO到GRPO的完整落地

强化学习&#xff08;RL&#xff09;在昇腾NPU上训练比监督学习复杂得多。你需要同时跑策略网络、价值网络&#xff0c;维护动态的经验回放缓冲区&#xff0c;还要处理动态Shape和显存碎片化问题。 这篇将手把手教你如何在昇腾NPU上高效训练RL模型&#xff0c;涵盖PPO/GRPO算法…

作者头像 李华
网站建设 2026/5/25 2:36:06

Unity Timeline激活与动画控制实战:5分钟精准调度

1. 这不是“Timeline入门”&#xff0c;而是你真正能用上的控制逻辑很多人第一次点开Unity Timeline面板时&#xff0c;第一反应是&#xff1a;“这不就是个时间轴剪辑工具吗&#xff1f;跟AE差不多&#xff1f;”——然后转身就去写Update里硬编码的if-else开关&#xff0c;或…

作者头像 李华