C++ STL 概念
STL(标准模板库,Standard Template Library) ,是 C++ 提供的标准模板类库,核心定义如下:
- 它以模板泛型编程的方式,对常用数据结构(动态数组、链表、哈希表、红黑树、栈、队列等)进行封装;
- 同时提供通用的泛型算法(排序、查找、遍历、拷贝等),实现数据结构与算法解耦;
- 核心由容器、迭代器、算法、函数对象、适配器、空间配置器六大部分组成,可跨类型复用,大幅提升开发效率。
C++ STL 容器分类
一、容器整体分类
1. 序列式容器(底层为线性结构,元素有序、可重复)
表格
| 容器 | 底层数据结构 | 版本 | 核心特点 |
|---|---|---|---|
string |
字符顺序表 | C++98 | 专门处理字符串 |
vector |
动态顺序表(连续内存) | C++98 | 随机访问快,尾部增删高效 |
list |
双向循环链表 | C++98 | 任意位置插入删除快,不支持随机访问 |
deque |
分段连续数组(动态二维数组) | C++98 | 头尾增删高效,支持随机访问 |
array |
静态顺序表 | C++11 | 固定大小数组,栈上分配 |
forward_list |
单向循环链表 | C++11 | 空间开销更小,仅支持单向遍历 |
2. 关联式容器(底层树形 / 哈希,自动排序 / 快速查找)
(1)树形结构(C++98,红黑树,有序)
map:键值对,key 唯一,默认升序set:纯 key 集合,元素唯一,默认升序multimap:键值对,key 可重复multiset:纯 key 集合,元素可重复
(2)哈希结构(C++11,哈希表,无序,查找更快)
unordered_map:无序键值对,key 唯一unordered_set:无序集合,元素唯一unordered_multimap:无序键值对,key 可重复unordered_multiset:无序集合,元素可重复
二、面试高频考点(思维导图 5 点核心)
1. 容器常用接口
通用:size()、empty()、clear()、begin()、end()
vector:push_back()、pop_back()、reserve()、resize()list:push_front()、pop_front()、splice()map/unordered_map:insert()、erase()、find()、operator[]
2. 底层数据结构(必背)
- 连续内存:
vector、array、string - 链表:
list、forward_list - 分段数组:
deque - 红黑树:
map/set/multimap/multiset - 哈希表:
unordered_*系列
3. 应用场景
- 频繁随机访问、尾部增删 →
vector - 频繁任意位置插入删除 →
list - 头尾高频操作 →
deque - 有序键值查找 →
map - 快速无序查找 →
unordered_map
4. 高频容器区别(面试必问,精简版)
① vector vs list
- 底层:vector 动态数组;list 双向链表
- 访问:vector 支持随机访问
[];list 不支持 - 增删:vector 中间插入删除 O (n);list 任意位置 O (1)
- 内存:vector 连续内存;list 节点分散,碎片多
② map vs set
- map 存储
<key,value>键值对;set 只存储 key - map 可通过 key 映射 value;set 用于去重、排序
③ map vs unordered_map
- 底层:map 红黑树;unordered_map 哈希表
- 有序性:mapkey 有序;unordered_map 无序
- 效率:map 查找 O (logn);unordered_map 平均 O (1)
- 稳定性:map 无哈希冲突;unordered_map 存在哈希冲突
5. 接口时间复杂度
vector随机访问:O (1);中间插入:O (n)list插入删除:O (1);查找:O (n)map查找 / 插入 / 删除:O (logn)unordered_map平均 O (1),最坏 O (n)
C++ STL 迭代器
一、迭代器核心概念与作用
1. 迭代器模式概念
迭代器是行为型设计模式,提供一种统一方式遍历容器元素,无需暴露容器内部结构。
2. 迭代器作用
解耦容器 与算法,让算法可以透明操作容器数据,不用关心容器底层是数组、链表还是树结构,实现一套算法适配所有容器。
3. 迭代器本质
本质是指针的封装,是容器和算法之间的桥梁,模拟指针行为。
二、迭代器分类(面试必背)
1. 按功能遍历方向分类
表格
| 迭代器类型 | 移动能力 | 支持容器 |
|---|---|---|
| 正向迭代器 | 只能 ++ 向后遍历 |
所有 STL 容器 |
| 反向迭代器 | 只能 -- 向前遍历(rbegin()/rend()) |
vector/list/map 等 |
| 双向迭代器 | 支持 ++、-- 双向移动 |
list、map、set |
| 随机迭代器 | 支持 ++/--/+n/-n/[] 随机访问 |
vector、deque、string |
权限等级:随机迭代器 > 双向迭代器 > 正向迭代器
2. 按能否修改元素分类
- 普通迭代器:可读可写,可修改指向的元素
- const 迭代器:只读不可写,禁止修改元素,保证数据安全
三、如何给容器自定义迭代器(4 个核心重载要求)
- 构造:绑定容器底层结构(数组 / 链表节点等)
- 解引用与成员访问 :重载
operator*()、operator->(),模拟指针取值 - 迭代器移动 :重载前置 / 后置
operator++(),双向容器额外重载operator--() - 迭代器比较 :重载
operator==()、operator!=(),用于判断遍历边界
四、面试高频延伸考点(加分项)
- 失效问题
vector:扩容、中间插入删除会导致迭代器失效list:仅删除当前迭代器会失效,其余不受影响
- 底层对应
- 随机迭代器:原生指针即可实现
- 双向迭代器:封装链表节点指针
C++ STL 算法
一、算法整体分类
1. 与数据结构相关算法
指容器自带的成员方法,和容器底层强绑定,只能对应容器使用。
- 例:
list::sort()、map::find()、vector::erase()
2. 通用算法(<algorithm>头文件)
不依赖具体容器,通过迭代器操作数据,一套算法适配所有容器,分为两类:
- 不带仿函数的算法 :使用默认规则(如默认升序排序、默认相等判断)
- 例:
sort、find、reverse、copy
- 例:
- 带有仿函数的算法 :支持传入仿函数 / 函数指针 /lambda ,自定义算法逻辑
- 例:
sort(v.begin(),v.end(),greater<int>())降序排序;可自定义比较规则、筛选规则
- 例:
二、使用注意事项
- 头文件依赖 :通用算法必须包含头文件
<algorithm> - 常见高频算法(面试必背)
- 排序类:
sort(快排,O (nlogn))、stable_sort(稳定排序) - 查找类:
find、binary_search(二分查找,需有序) - 遍历修改:
for_each、transform - 最值 / 计数:
max_element、min_element、count
- 排序类:
- 时间复杂度(核心考点)
sort:O(nlogn)find:O(n)binary_search:O(logn)for_each:O(n)
C++ STL 适配器
一、适配器模式概念
适配器是结构型设计模式 ,作用是复用已有组件,通过封装转换接口 ,让原有组件适配新的使用场景,不用修改底层实现。STL 适配器不自己实现底层结构,而是基于已有容器 / 迭代器 / 函数做二次封装。
二、三大分类(面试必背)
1. 容器适配器(最常考)
对已有序列式容器进行封装,限定接口,实现特定数据结构:
表格
| 适配器 | 默认底层容器 | 核心特点 |
|---|---|---|
stack(栈) |
deque |
后进先出 LIFO,只允许栈顶增删查 |
queue(队列) |
deque |
先进先出 FIFO,只允许头尾操作 |
priority_queue(优先队列) |
vector |
堆结构,默认大顶堆,自动维护最值 |
底层可手动更换:例
stack<int, vector<int>> st;
2. 迭代器适配器
对普通迭代器封装,改变遍历行为:
- 反向迭代器 :
reverse_iterator,如rbegin()/rend(),反转遍历方向 - 插入迭代器、流迭代器等,适配不同遍历 / 插入场景
3. 函数适配器
对函数对象 / 函数指针封装,适配算法参数要求:
- 常见:
bind、not1、not2、mem_fun等,用于绑定参数、取反逻辑、适配成员函数
三、面试高频考点
- stack/queue 为什么不能用 list 做底层? 可以用,只是默认选
deque,因为deque头尾操作效率最高。 - priority_queue 底层是堆,堆是大顶堆还是小顶堆? 默认大顶堆 ,可通过
greater仿函数改为小顶堆。 - 适配器本质:复用现有组件,只修改对外接口,不修改底层实现。
C++ STL 仿函数
一、核心概念
仿函数(函数对象):让一个类的对象可以像普通函数一样调用使用 ,本质是重载了 operator() 的类。
二、实现方式
在自定义类中,重载圆括号运算符 operator(),即可将类对象变为可调用的仿函数。
极简示例
cpp
struct Compare {
bool operator()(int a, int b) {
return a > b; // 自定义比较逻辑
}
};
三、核心作用
- 定制 STL 通用算法功能 :给
sort、find、priority_queue等算法传入自定义逻辑(排序规则、匹配规则),让算法灵活可扩展。 - 相比普通函数的优势 :
- 可保存状态(类成员变量);
- 类型安全,编译器可做类型检查;
- 可被适配器修饰,适配更多场景。
四、面试高频考点
- 和普通函数、lambda 的区别
- 普通函数:无状态,不可适配;
- 仿函数:可存状态,兼容性强;
- lambda:语法简洁,本质也是匿名仿函数。
- 典型使用场景
sort(v.begin(),v.end(),Compare())自定义排序;priority_queue<int,vector<int>,greater<int>>小顶堆。
C++ STL 空间配置器
1. 什么是空间配置器 & 作用
概念
空间配置器(allocator)是 STL 的内存管理组件,负责容器的内存申请、释放、对象构造、析构,隔离容器与底层内存操作。
作用
- 统一管理容器内存,将内存分配 和对象构造分离;
- 优化小块内存分配效率,减少内存碎片;
- 屏蔽平台内存差异,实现跨平台。
2. 为什么需要空间配置器
- STL 容器频繁创建 / 销毁大量小块内存 ,直接用
malloc/free效率低、易产生内存碎片; - 普通内存分配仅分配空间,配置器可同时处理内存分配 + 对象构造;
- 针对大小块内存做分层优化,提升性能。
3. SGI‑STL 两级空间配置器实现原理
一级空间配置器
- 应用场景 :处理大于 128 字节的大块内存
- 原理 :直接封装
malloc、free,额外增加内存不足时的异常处理机制(内存池兜底 / 抛出异常)
二级空间配置器(重点)
- 应用场景:处理 **≤128 字节 ** 的小块内存
- 核心原理 :
- 封装内存池,预先申请一大块连续内存,拆分复用;
- 维护16 个哈希桶,分别管理 8/16/24...128 字节的内存块;
- 小块内存申请 / 释放直接从对应哈希桶取放,避免频繁调用系统接口,减少碎片。
4. 空间配置器优缺点
优点
- 小块内存分配速度更快,减少系统调用开销;
- 有效减少内存碎片;
- 内存分配与对象构造分离,灵活可控。
缺点
- 内存池会占用部分闲置内存,存在一定内存浪费;
- 二级配置器仅针对小块内存优化,大块内存无提升。