0

0

C++ STL中的deque容器底层是怎么实现的?(分段连续内存结构)

尼克

尼克

发布时间:2026-01-11 12:59:37

|

314人浏览过

|

来源于php中文网

原创

deque底层是分段连续内存而非链表,由固定大小chunk和中央索引数组map构成;push_front/push_back摊还O(1)因map预留扩展空间;随机访问O(1)但常数更大,需两次寻址。

c++ stl中的deque容器底层是怎么实现的?(分段连续内存结构)

deque 的底层确实是分段连续内存,但不是链表

很多人看到“分段”就默认是 std::list 那种指针链接的离散节点,其实 deque 完全不是。它用的是固定大小的内存块(通常叫“chunk”或“buffer”),每个块内部连续,块与块之间通过一个中央索引数组(map)间接寻址。这个 map 本身也是动态分配的连续数组,存的是一组指向各 chunk 起始地址的指针。

为什么 push_front / push_back 都是摊还 O(1)?

关键在于 map 数组预留了前后扩展空间。当在头部插入导致第一个 chunk 满了,deque 不会立刻重分配整个 map,而是检查 map 开头是否还有空槽;有就分配新 chunk 并更新对应槽位指针。尾部同理。只有 map 自身也满了,才触发一次 map 扩容(复制指针、重新分配更大数组),这就是摊还成本的来源。

  • push_frontpush_back 在绝大多数情况下只操作已有 chunk 或 map 空槽,不涉及内存拷贝
  • 真正昂贵的操作是 insert 中间位置(比如 deque.begin() + N),它需要移动大量元素 —— 因为跨 chunk 时无法像 vector 那样用 memmove,必须逐个构造/析构
  • 迭代器失效规则比 vector 更宽松:仅在插入/删除导致 chunk 边界变化时,才使指向被移动元素的迭代器失效;而 map 扩容时,所有迭代器都会失效(因为 chunk 指针数组重排了)

随机访问为什么仍是 O(1),但常数比 vector 大?

访问 deque[i] 需要两步计算:

// 伪代码示意(实际实现因库而异,但逻辑一致)
size_t chunk_idx = i / CHUNK_SIZE;
size_t offset_in_chunk = i % CHUNK_SIZE;
T* chunk_ptr = map[chunk_idx];  // 第一次间接寻址
return *(chunk_ptr + offset_in_chunk);  // 第二次指针偏移

相比 vector 的单次线性地址计算,deque 多了一次查表(map 数组索引)和一次指针解引用。CHUNK_SIZE 通常是常量(如 512 字节,即 64 个 int),它平衡了内存浪费(小 chunk → map 太大)和局部性(大 chunk → 接近 vector 表现)。

立即学习C++免费学习笔记(深入)”;

ImgCreator AI
ImgCreator AI

一款AI图像生成工具,适合创建插图、动画和概念设计图像。

下载
  • glibc 的 libstdc++ 默认 CHUNK_SIZE 是 sizeof(T)
  • MSVC 的 deque 使用 16 字节对齐的固定 chunk 大小(如 4096 字节),与元素类型无关
  • 不要假设 &d[0]&d[1] 地址连续 —— 它们可能在不同 chunk 里

哪些操作会意外触发 map 或 chunk 重分配?

最隐蔽的坑是构造时指定 size:

std::deque d(1000000);  // 可能立即分配数十个 chunk + map 数组

这不像 vector 那样只分配一块内存,deque 必须预估 chunk 数量并分配 map。另一个易忽略点是 shrink_to_fit() —— deque 标准根本没定义这个成员函数,调用会编译失败。清空后想释放内存?只能靠作用域结束或手动 swap

std::deque d;
// ... use ...
std::deque().swap(d);  // 强制释放所有 chunk 和 map
  • clear() 只销毁元素,不释放任何 chunk 或 map 内存
  • resize(0) 效果同 clear(),不释放底层存储
  • 频繁 push_front 后又长期只读访问?内存布局可能非常稀疏,cache miss 率显著高于等价的 vector

真正理解 deque,得记住它本质是“带索引的 chunk 链”,不是链表也不是 vector 的变体。它的设计目标很明确:在保持随机访问能力的前提下,让首尾增删不退化。但代价是更复杂的内存布局和更高的访问常数 —— 这些细节在调试性能瓶颈或排查迭代器失效时,往往就是关键。

相关专题

更多
java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

1463

2023.10.24

string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

315

2023.08.02

int占多少字节
int占多少字节

int占4个字节,意味着一个int变量可以存储范围在-2,147,483,648到2,147,483,647之间的整数值,在某些情况下也可能是2个字节或8个字节,int是一种常用的数据类型,用于表示整数,需要根据具体情况选择合适的数据类型,以确保程序的正确性和性能。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

534

2024.08.29

c++怎么把double转成int
c++怎么把double转成int

本专题整合了 c++ double相关教程,阅读专题下面的文章了解更多详细内容。

51

2025.08.29

C++中int的含义
C++中int的含义

本专题整合了C++中int相关内容,阅读专题下面的文章了解更多详细内容。

194

2025.08.29

golang map内存释放
golang map内存释放

本专题整合了golang map内存相关教程,阅读专题下面的文章了解更多相关内容。

73

2025.09.05

golang map相关教程
golang map相关教程

本专题整合了golang map相关教程,阅读专题下面的文章了解更多详细内容。

28

2025.11.16

golang map原理
golang map原理

本专题整合了golang map相关内容,阅读专题下面的文章了解更多详细内容。

59

2025.11.17

c++主流开发框架汇总
c++主流开发框架汇总

本专题整合了c++开发框架推荐,阅读专题下面的文章了解更多详细内容。

80

2026.01.09

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
CSS3 教程
CSS3 教程

共18课时 | 4.4万人学习

Sass 教程
Sass 教程

共14课时 | 0.8万人学习

Pandas 教程
Pandas 教程

共15课时 | 0.9万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号