C++ STL 常考面试题总结
1. std::map 和 std::unordered_map 的区别(含底层实现)
2. std::set 和 std::unordered_set 的区别
核心逻辑与 map/unordered_map 一致,核心差异在于存储结构:
- std::set:底层红黑树,存储唯一键值(键即值),有序、增删查 ;
- std::unordered_set:底层哈希表,存储唯一键值,无序、平均增删查 ;
- 补充:二者均不允许重复元素,如需重复可使用 multiset/unordered_multiset。
3. map 和 set 的区别及实现方式
底层实现
二者底层均为红黑树,均遵循二叉搜索树的特性,保证元素有序和操作的 复杂度。
核心区别
4. vector 和 list 的区别及适用场景
适用场景
- vector:需频繁随机访问、尾部插入删除、数据量固定或扩容少的场景(如算法刷题、存储有序序列);
- list:需频繁在任意位置插入删除、数据量动态变化大的场景(如链表操作、任务队列)。
5. std::deque 与 vector 的区别及适用场景
6. std::string 的底层实现及优化
- 底层实现:多数标准库采用短字符串优化(SSO),短字符串直接存储在对象内部,长字符串则指向堆上的字符数组;
- 核心特性:支持随机访问、尾部插入高效,resize/reserve 与 vector 类似;
- 优化点:避免频繁扩容(预分配空间)、使用 std::string_view 避免不必要的拷贝。
7. 什么是迭代器失效,如何避免?
定义
迭代器本质是容器元素的“指针/引用”,当容器底层内存结构改变或元素位置移动时,迭代器指向的地址失效,访问会触发未定义行为。
常见失效场景与避免方法
8. STL 迭代器如何删除元素?
核心原则:通过容器的 erase 方法删除,而非迭代器自身,且需用 erase 的返回值更新迭代器,避免失效。
核心代码示例
// vector 删除元素(避免失效)
vector<int> vec = {1, 2, 3, 4};
for (auto it = vec.begin(); it != vec.end();) {
if (*it == 2) {
it = vec.erase(it); // 用返回值更新迭代器,指向被删元素的下一个
} else {
it++;
}
// map 删除元素(erase(it++) 写法)
map<int, string> mp = {{1, "a"}, {2, "b"}};
for (auto it = mp.begin(); it != mp.end();) {
if (it->first == 2) {
mp.erase(it++); // 先移动迭代器,再删除原位置元素
} else {
it++;
}
}
9. 如何删除 std::vector 中的特定元素?
方法一:遍历+erase(适合少量删除)
vector<int> vec = {1, 2, 2, 3};
for (auto it = vec.begin(); it != vec.end();) {
if (*it == 2) it = vec.erase(it);
else it++;
}
方法二:erase-remove 惯用法(适合大量删除,效率更高)
核心:remove 仅将非删除元素移到容器前部,返回新尾迭代器;erase 删除尾部冗余元素。
#include <algorithm>
vector<int> vec = {1, 2, 2, 3};
// 删除所有值为2的元素
vec.erase(remove(vec.begin(), vec.end(), 2), vec.end());
10. resize 和 reserve 的区别(针对 vector)
二者均针对 vector(string 同理),核心差异在于操作目标不同:
vector<int> vec;vec.reserve(10); // capacity=10,size=0vec.resize(5); // capacity=10,size=5,元素为默认值0
11. STL 迭代器的分类及特点
- 输入迭代器:只读,单次遍历(如 istream_iterator);
- 输出迭代器:只写,单次遍历(如 ostream_iterator);
- 前向迭代器:可读写,支持多次遍历(如 forward_list 的迭代器);
- 双向迭代器:支持前后遍历(如 list/map/set 的迭代器);
- 随机访问迭代器:支持随机访问、下标操作(如 vector/deque/string 的迭代器)。
12. std::advance 和 std::next 的区别
- std::advance(it, n):将迭代器 it 前进 n 步,无返回值,会修改原迭代器;
- std::next(it, n):返回迭代器 it 前进 n 步后的新迭代器,不修改原迭代器;
- 注意:advance 对双向迭代器支持负数步长,对随机访问迭代器效率为 ,对其他迭代器为 。
13. STL 容器的时间复杂度(vector、list、map、unordered_map)
14. vector 的实现原理
vector 是动态连续数组,核心由三个迭代器(指针)实现:
- start:指向数组起始位置;
- finish:指向最后一个有效元素的下一个位置;
- end_of_storage:指向内存缓冲区的末尾。
核心机制
- 扩容机制:当插入元素导致 size() == capacity() 时,触发扩容(通常为原容量的 2 倍或 1.5 倍),步骤为:分配新内存 → 拷贝/移动原元素 → 释放原内存 → 更新指针;
- 内存管理:通过分配器(allocator)管理内存,实现内存分配与对象构造分离;
- 尾插/尾删:直接操作 finish 指针,未扩容时效率极高。
15. map 的实现原理
std::map 底层基于红黑树(一种自平衡的二叉搜索树)实现,核心特性:
- 有序性:按键的升序排列,二叉搜索树的中序遍历即为有序序列;
- 自平衡性:通过变色、旋转(左旋/右旋)保证树的高度为 ,避免退化为链表;
- 存储结构:每个节点存储 std::pair<const Key, T>,键不可修改,保证二叉搜索树的特性;
- 操作逻辑:增删查改均通过二叉搜索树的特性实现,时间复杂度稳定在 。
16. vector 与 list 的具体实现及常见操作时间复杂度
std::vector 实现与复杂度
- 实现:动态连续数组,由起始指针、有效尾指针、容量尾指针控制,依赖分配器管理内存,扩容时重新分配内存
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
C++ 常考面试题总结 文章被收录于专栏
本专栏系统梳理C++方向, 大中厂高频高频面试考点 , 内容皆来自真实面试经历,从基础语法、内存管理、STL与设计模式,到操作系统与项目实战,结合真实面试题深度解析,帮助开发者高效查漏补缺,提升技术理解与面试通过率,打造扎实的C++工程能力.