🎬 HoRain 云小助手:个人主页
⛺️生活的理想,就是为了理想的生活!
⛳️ 推荐
前些天发现了一个超棒的服务器购买网站,性价比超高,大内存超划算!忍不住分享一下给大家。点击跳转到网站。
目录
⛳️ 推荐
📚 C++ STL 教程:从"能跑"到"不踩坑"
🎯 STL 三大件一句话
📦 六大容器家族速查
1️⃣ vector—— 动态数组(≈ Java ArrayList)
2️⃣ list—— 双向链表(≈ Java LinkedList)
3️⃣ deque—— 双端队列(Java 没有严格对等)
4️⃣ map—— 红黑树(≈ Java TreeMap)
5️⃣ unordered_map—— 哈希表(≈ Java HashMap)
6️⃣ set/ unordered_set—— 只存 key 不存 value
🔄 迭代器(C++ 独有概念,Java 只有 Iterator一种)
🧵 string—— 不是 STL 容器但像容器
⚠️ 迭代器失效(C++ 特有,Java 没有这坑)
🆚 跟 Java 集合对照表(你前面 Java LinkedList/String 都看过)
🧩 一段"能打"的综合示例
🎯 学习路线建议
📚 C++ STL 教程:从"能跑"到"不踩坑"
STL(Standard Template Library)是 C++ 的"武器库",你前面看完类/多态/动态内存,STL 是下一块——它跟 Java 集合框架(Collections Framework)能对上,但 C++ 多了"迭代器 + 算法分离"这套设计,是 C++ 特有的味道。
🎯 STL 三大件一句话
组件 | 干嘛的 | 类比 Java |
|---|---|---|
容器(Containers) | 存数据 |
|
算法(Algorithms) | 操作数据( |
|
迭代器(Iterators) | 容器和算法之间的胶水 |
|
C++ 的设计哲学:容器只管存,算法只管算,迭代器把它们粘起来。Java 的
ArrayList.sort()是把算法绑在容器上的,两套思路。
📦 六大容器家族速查
1️⃣vector—— 动态数组(≈ JavaArrayList)
最常用,没有之一。连续内存,随机访问 O(1),尾插 O(1) 均摊。
#include <vector> #include <iostream> std::vector<int> v = {1, 2, 3}; v.push_back(4); // 尾插 O(1) v.emplace_back(5); // 比 push_back 更高效(就地构造) std::cout << v[2]; // 3,不越界检查 std::cout << v.at(10); // 抛 std::out_of_range(慢一点但安全) // 遍历 for (int x : v) std::cout << x << " "; // range-for(C++11)💡
emplace_backvspush_back:push_back(T(val))是先构造临时对象再移动,emplace_back(val)直接在容器里构造,少一次拷贝/移动,对象重时明显。
2️⃣list—— 双向链表(≈ JavaLinkedList)
#include <list> std::list<int> lst = {1, 2, 3}; lst.push_front(0); // 头插 O(1) lst.push_back(4); // 尾插 O(1) lst.reverse(); // 链表专属
list没有[]随机访问,要取第 i 个得std::advance(it, i)走 i 步 O(n)。
3️⃣deque—— 双端队列(Java 没有严格对等)
头尾插删都 O(1),比vector多一个头插能力,比list多随机访问。
#include <deque> std::deque<int> dq; dq.push_front(1); dq.push_back(2); std::cout << dq[0]; // 1,能随机访问
stack/queue底层默认用deque。
4️⃣map—— 红黑树(≈ JavaTreeMap)
按键有序,O(log n) 查插删。
#include <map> #include <string> std::map<std::string, int> scores = { {"Alice", 90}, {"Bob", 85} }; scores["Charlie"] = 95; // 插入/修改 int a = scores["Alice"]; // 90,不存在会插入默认值! // 安全查 if (scores.contains("Dave")) { // C++20 // ... } auto it = scores.find("Dave"); // C++11 起最佳做法 if (it != scores.end()) { // ... }5️⃣unordered_map—— 哈希表(≈ JavaHashMap)
无序,平均 O(1),比map快但要算 hash。
#include <unordered_map> std::unordered_map<std::string, int> um = { {"Apple", 10}, {"Banana", 5} }; um["Orange"] = 8; std::cout << um["Apple"]; // 10📌 选型:要顺序/范围查询 →
map;要纯 KV 查 →unordered_map。跟 Java 的TreeMapvsHashMap完全同逻辑。
6️⃣set/unordered_set—— 只存 key 不存 value
#include <set> std::set<int> s = {3, 1, 2}; s.insert(4); for (int x : s) std::cout << x << " "; // 1 2 3 4(有序)🔄 迭代器(C++ 独有概念,Java 只有Iterator一种)
std::vector<int> v = {10, 20, 30}; // 经典写法 for (auto it = v.begin(); it != v.end(); ++it) { std::cout << *it << " "; } // C++11 range-for(底层就是迭代器) for (int x : v) { std::cout << x << " "; } // C++20 ranges(更爽,但先不展开)迭代器分类(面试会问):
输入 / 输出:单向,只读/只写
前向:单向,可读写
双向:
--it也能走(list,map,set)随机访问:
it + 5,it1 - it2(vector,deque,string)
💡
vector的迭代器就是指针,list的迭代器是节点指针包装——所以vector能随机访问,list不能。
🛠️ 算法<algorithm>(STL 的精华)
#include <algorithm> #include <vector> #include <iostream> std::vector<int> v = {3, 1, 4, 1, 5}; // 排序 std::sort(v.begin(), v.end()); // 升序 std::sort(v.begin(), v.end(), std::greater<int>()); // 降序 // 查找 auto it = std::find(v.begin(), v.end(), 4); // 返回迭代器 if (it != v.end()) std::cout << "found\n"; // 计数 int cnt = std::count(v.begin(), v.end(), 1); // 2 // 遍历修改(C++11 lambda) std::for_each(v.begin(), v.end(), [](int x) { std::cout << x << " "; }); // 变换到新容器 std::vector<int> doubled(v.size()); std::transform(v.begin(), v.end(), doubled.begin(), [](int x) { return x * 2; });跟 Java 对比:
std::sort(v.begin(), v.end())≈Collections.sort(list),但 C++ 的算法是独立于容器的——同一个sort能排vector/deque/ 原生数组,只要给随机访问迭代器。
🧵string—— 不是 STL 容器但像容器
你前面看了 JavaString(不可变),C++std::string是可变的,这点最容易被 Java 人搞混。
#include <string> std::string s = "hello"; s += ", world"; // ✅ 可变,Java 这步要 new s[0] = 'H'; // "Hello, world" // 常用 s.size(); // 长度 s.empty(); // 是否空 s.substr(0, 5); // "Hello"(左闭右开) s.find("world"); // 7,没找到返回 std::string::npos // C++14 起 string 也是连续存储,能当 `vector<char>` 用但语义更清楚⚠️ C++
std::string≠ JavaString:Java 不可变+常量池,C++ 可变+值语义+可reserve()。混着记会踩坑。
⚠️ 迭代器失效(C++ 特有,Java 没有这坑)
改容器结构可能导致迭代器作废——这是 STL 最容易翻车的地方。
std::vector<int> v = {1, 2, 3, 4, 5}; for (auto it = v.begin(); it != v.end(); ++it) { if (*it == 3) { v.erase(it); // ❌ it 已失效,下次 ++it UB } } // ✅ 正确:erase 返回下一个有效迭代器 for (auto it = v.begin(); it != v.end(); ) { if (*it == 3) { it = v.erase(it); // erase 返回下一个 } else { ++it; } }各容器失效规则速记:
操作 | vector | deque | list | map/set | unordered_* |
|---|---|---|---|---|---|
尾插 | 尾 iter 可能失效 | 全有效 | 全有效 | 全有效 | 可能全失效(rehash) |
中间删 | 被删及之后失效 | 被删及之后失效 | 只被删失效 | 只被删失效 | 只被删失效 |
一句话:
vector最娇气,list/map最稳,unordered_*rehash 时全炸。
🆚 跟 Java 集合对照表(你前面 Java LinkedList/String 都看过)
C++ STL | Java 集合 | 备注 |
|---|---|---|
|
| 连续数组,随机访问 |
|
| 双向链表 |
| 无严格对等 |
|
|
| 红黑树,有序 |
|
| 哈希,无序 |
| 无直接对等 |
|
|
| C++ 算法独立于容器 |
|
| C++ 可变,Java String 不可变 |
🧩 一段"能打"的综合示例
#include <iostream> #include <vector> #include <unordered_map> #include <algorithm> #include <string> struct Student { std::string name; int score; }; int main() { std::vector<Student> students = { {"Alice", 90}, {"Bob", 85}, {"Charlie", 95} }; // 按分数降序 std::sort(students.begin(), students.end(), [](const Student& a, const Student& b) { return a.score > b.score; }); // 统计分数分布 std::unordered_map<int, int> dist; for (const auto& s : students) { dist[s.score / 10 * 10]++; // 90分段、80分段... } for (const auto& [range, cnt] : dist) { std::cout << range << "-" << range+9 << ": " << cnt << "\n"; } }🎯 学习路线建议
STL 不用背全(一百多个算法没人全记得),容器会挑 + 迭代器懂失效 + 算法记
sort/find/transform就够 80% 场景。
顺着你这条线(类 → 抽象 → 多态 → 动态内存 → STL),下一块自然落到C++11 新特性(auto、lambda、范围 for、智能指针你已经看过一部分),或者STL 源码浅读(vector扩容 /unordered_map冲突)——前者偏"写代码爽",后者偏面试八股。要哪边?
❤️❤️❤️本人水平有限,如有纰漏,欢迎各位大佬评论批评指正!😄😄😄
💘💘💘如果觉得这篇文对你有帮助的话,也请给个点赞、收藏下吧,非常感谢!👍 👍 👍
🔥🔥🔥Stay Hungry Stay Foolish 道阻且长,行则将至,让我们一起加油吧!🌙🌙🌙