当前位置: 首页 > news >正文

std::deque的简化源码详解


1. 基本结构与成员变量

源码片段

template <typename T, typename Alloc = std::allocator<T>>
class deque {
private:static const size_type block_size = 16; // 每个块的大小using block_type = T*;                  // 块类型using map_type = std::vector<block_type>; // 映射类型map_type map;       // 存储块指针的映射size_type map_size; // 映射大小size_type start;    // 第一个元素的块索引size_type finish;   // 最后一个元素的块索引size_type first;    // 第一个元素在块中的索引size_type last;     // 最后一个元素在块中的索引Alloc alloc;        // 分配器
};

详解

  • block_size:每个内存块固定容纳 16 个元素,这是 deque 分块存储的基础。
  • block_type:每个块是一个 T* 类型的指针,指向一块连续内存。
  • map_type:使用 std::vector 存储所有块的指针,形成映射表。
  • map:核心数据结构,管理所有块的指针。
  • map_size:映射表当前的大小。
  • startfinish:分别标记首块和尾块在 map 中的位置。
  • firstlast:首块和尾块中元素的具体偏移量。
  • alloc:内存分配器,用于动态管理内存。

2. 构造函数

源码片段

deque() : map(), map_size(0), start(0), finish(0), first(0), last(0) {map.push_back(allocate_block());map_size = 1;
}

详解

  • 默认构造函数初始化一个空 deque
  • map 中插入一个新分配的内存块,map_size 设为 1。
  • startfinish 指向同一块,firstlast 为 0,表示当前无元素。

3. 尾部插入(push_back)

源码片段

void push_back(const T& value) {if (last == block_size || finish == map_size - 1) {if (finish == map_size - 1) {reallocate_map(map_size * 2 + 1);}map[++finish] = allocate_block();last = 0;}std::allocator_traits<Alloc>::construct(alloc, map[finish] + last, value);++last;
}

详解

  • 检查当前块是否满(last == block_size)或映射表是否无空间(finish == map_size - 1)。
  • 如果映射表满,调用 reallocate_map 扩展容量。
  • 分配新块并更新 finishlast 重置为 0。
  • 在尾块的 last 位置构造新元素,last 递增。

4. 头部插入(push_front)

源码片段

void push_front(const T& value) {if (first == 0) {if (start == 0) {reallocate_map(map_size * 2 + 1);start = map_size / 2;finish += start;}map[--start] = allocate_block();first = block_size;}std::allocator_traits<Alloc>::construct(alloc, map[start] + (--first), value);
}

详解

  • 如果首块无空间(first == 0),检查映射表头部是否可用。
  • 若不可用,扩展映射表并调整 startfinish
  • 分配新块,start 递减,first 设为 block_size
  • 在首块的 first-1 位置构造元素。

5. 尾部删除(pop_back)

源码片段

void pop_back() {if (last == 0) {if (finish > start) {deallocate_block(map[finish--]);last = block_size;}}if (last > 0) {std::allocator_traits<Alloc>::destroy(alloc, map[finish] + (--last));}
}

详解

  • 若尾块为空(last == 0)且有多块,释放尾块并更新 finishlast
  • 若尾块有元素,销毁最后一个元素,last 递减。

6. 头部删除(pop_front)

源码片段

void pop_front() {if (first == block_size - 1) {if (start < finish) {deallocate_block(map[start++]);first = 0;}}if (first < block_size) {std::allocator_traits<Alloc>::destroy(alloc, map[start] + (first++));}
}

详解

  • 若首块将空(first == block_size - 1)且有多块,释放首块并更新 startfirst
  • 若首块有元素,销毁第一个元素,first 递增。

7. 元素访问(operator[])

源码片段

reference operator[](size_type n) {size_type index = first + n;size_type block_index = start + index / block_size;size_type block_offset = index % block_size;return map[block_index][block_offset];
}

详解

  • 计算全局索引 index,然后确定块索引和块内偏移。
  • 返回对应位置的元素引用,支持随机访问。

8. 大小计算(size)

源码片段

size_type size() const {return (finish - start) * block_size + last - first;
}

详解

  • 计算中间完整块的元素数,加上首尾块的元素数,得出总大小。

9. 映射重新分配(reallocate_map)

源码片段

void reallocate_map(size_type new_size) {map_type new_map(new_size, nullptr);std::copy(map.begin(), map.end(), new_map.begin());map.swap(new_map);map_size = new_size;
}

详解

  • 创建新映射表,复制原有块指针,交换并更新 map_size

10. 完整源码示例

#include <vector>
#include <memory>
#include <stdexcept>
#include <cassert>template <typename T, typename Alloc = std::allocator<T>>
class deque {
public:using value_type = T;using allocator_type = Alloc;using size_type = std::size_t;using reference = value_type&;using const_reference = const value_type&;using pointer = typename std::allocator_traits<Alloc>::pointer;private:static const size_type block_size = 16; // Size of each blockusing block_type = T*;                  // Block typeusing map_type = std::vector<block_type>; // Map type for block pointersmap_type map;       // Stores block pointerssize_type map_size; // Size of the mapsize_type start;    // Index of the first blocksize_type finish;   // Index of the last blocksize_type first;    // Offset of the first element in the start blocksize_type last;     // Offset of the last element in the finish blockAlloc alloc;        // Allocator// Allocate a new blockblock_type allocate_block() {return alloc.allocate(block_size);}// Deallocate a blockvoid deallocate_block(block_type block) {alloc.deallocate(block, block_size);}// Reallocate the map with a new sizevoid reallocate_map(size_type new_size) {map_type new_map(new_size, nullptr);size_type old_map_size = map.size();size_type offset = (new_size - old_map_size) / 2; // Center the existing blocksstd::copy(map.begin(), map.end(), new_map.begin() + offset);map.swap(new_map);start += offset;finish += offset;map_size = new_size;}// Destroy elements in a block within a rangevoid destroy_elements(block_type block, size_type begin, size_type end) {for (size_type i = begin; i < end; ++i) {std::allocator_traits<Alloc>::destroy(alloc, block + i);}}public:// Default constructordeque() : map(), map_size(0), start(0), finish(0), first(0), last(0) {map.push_back(allocate_block());map_size = 1;}// Destructor~deque() {for (size_type i = start; i <= finish; ++i) {if (i == start) {destroy_elements(map[i], first, block_size);} else if (i == finish) {destroy_elements(map[i], 0, last);} else {destroy_elements(map[i], 0, block_size);}deallocate_block(map[i]);}}// Push element to the backvoid push_back(const T& value) {if (last == block_size) {if (finish == map_size - 1) {reallocate_map(map_size * 2 + 1);}map[++finish] = allocate_block();last = 0;}std::allocator_traits<Alloc>::construct(alloc, map[finish] + last, value);++last;}// Push element to the frontvoid push_front(const T& value) {if (first == 0) {if (start == 0) {reallocate_map(map_size * 2 + 1);}map[--start] = allocate_block();first = block_size;}std::allocator_traits<Alloc>::construct(alloc, map[start] + (--first), value);}// Pop element from the backvoid pop_back() {if (last == 0) {if (finish > start) {deallocate_block(map[finish--]);last = block_size;}}if (last > 0) {std::allocator_traits<Alloc>::destroy(alloc, map[finish] + (--last));}}// Pop element from the frontvoid pop_front() {if (first == block_size - 1) {if (start < finish) {deallocate_block(map[start++]);first = 0;}}if (first < block_size) {std::allocator_traits<Alloc>::destroy(alloc, map[start] + (first++));}}// Access element by indexreference operator[](size_type n) {size_type total_index = first + n;size_type block_index = start + total_index / block_size;size_type block_offset = total_index % block_size;return map[block_index][block_offset];}// Return size of the dequesize_type size() const {return (finish - start) * block_size + last - first;}
};int main() {// 测试默认构造函数deque<int> d1;assert(d1.size() == 0);// 测试 push_backdeque<int> d2;d2.push_back(1);d2.push_back(2);assert(d2.size() == 2);assert(d2[0] == 1);assert(d2[1] == 2);// 测试 push_frontdeque<int> d3;d3.push_front(1);d3.push_front(2);assert(d3.size() == 2);assert(d3[0] == 2);assert(d3[1] == 1);// 测试 pop_backdeque<int> d4;d4.push_back(1);d4.push_back(2);d4.pop_back();assert(d4.size() == 1);assert(d4[0] == 1);// 测试 pop_frontdeque<int> d5;d5.push_front(1);d5.push_front(2);d5.pop_front();assert(d5.size() == 1);assert(d5[0] == 1);// 测试混合操作deque<int> d6;d6.push_back(1);d6.push_front(0);d6.push_back(2);d6.pop_front();assert(d6.size() == 2);assert(d6[0] == 1);assert(d6[1] == 2);return 0;
}

11. 总结

std::deque 通过分块存储和动态映射表,实现了高效的双端操作和随机访问,是 STL 中兼顾性能与灵活性的重要容器。

相关文章:

  • 架构-数据库系统
  • Java基础集合 面试经典八股总结 [连载ing]
  • Java开发工具IntelliJ IDEA v2025.1——全面支持Java 24、整合AI
  • C++内存管理那些事
  • 树型结构(知识点梳理及例题精讲)
  • 一键多环境构建——用 Hvigor 玩转 HarmonyOS Next
  • Docker 部署 Redis:快速搭建高效缓存服务
  • 解决yarn install 报错 error \node_modules\electron: Command failed.
  • 【PVCodeNet】《Palm Vein Recognition Network Combining Transformer and CNN》
  • Unity MR开发:探索混合现实的无限可能 (VisionPro和HoloLens 2 对比)
  • 注意力机制:Transformer如何用“数学凝视“统治AI?
  • 深度学习物理信息神经网络PINN+大模型辅助编程​
  • continue插件实现IDEA接入本地离线部署的deepseek等大模型
  • Kafka消息可视化工具Offset Explorer
  • windows中kafka4.0集群搭建
  • STM32F103系列单片机寄存器操作和标准库操作
  • SpringCloud微服务架构设计与实践 - 面试实战
  • Web3中心化交易所钱包-批量地址生成业务
  • 【RocketMq源码篇-01】环境搭建、基本使用、可视化界面
  • ES6 模块化 与 CommonJS 的核心概念解析
  • 海南旅文局通报游客入住酒店港币被调包:成立调查组赴陵水调查
  • 仅退款正式成历史?仅退款究竟该不该有?
  • 常熟银行一季度净赚超10亿增逾13%,净息差较上年末下降0.1个百分点
  • 第三款在美获批的国产PD-1肿瘤药来了,影响多大?
  • 在黄岩朵云书院,邂逅陈丹燕与月季花的故事
  • 王旭任甘肃省副省长