STL容器:deque核心总结
deque属于顺序容器的一种,是一个双端队列,它允许在容器的两端进行快速插入和删除元素的操作。
deque虽然支持像vector一样样的按索引访问,但其随机访问效率不如vector。
std::deque适用于需要在首尾两端频繁进行插入和删除的场景。
deque的底层实现
deque的元素不连续存储,而是由多个固定大小的连续内存块组成,并通过一个主控map来管理。
_M_map是一个指针数组,其中每个元素(node)指向一块内存块。内存块才是deque 的储存空间主体。
deque:
- map:记录每个内存块的地址。
- map_size:内存块的个数。
- start迭代器:记录着map数组中首个内存块的信息。
- finish迭代器:记录着map数组中最后一个内存块的信息。
template<typename _Tp> class _Deque_base {
    typedef _Deque_iterator<_Tp> iterator;
protected:
    _Tp** _M_map;
    size_t _M_map_size;
    iterator _M_start;
    iterator _M_finish;
};
迭代器:
- cur:指向当前迭代器所代表的元素。
- first:指向当前数据块的起始位置。
- last:指向当前数据块的末尾位置。
- node:指向map数组中存储的指向当前数据块的位置。
template<class _Tp> struct _Deque_iterator {
    typedef _Tp** _Map_pointer;
    _Tp* _M_cur;
    _Tp* _M_first;
    _Tp* _M_last;
    _Map_pointer _M_node;
};

初始状态
初始时,_M_map成员为nullptr , _M_map_size为0。
插入元素
以尾部插入为例:
- 首先,deque 检查 _M_finish._M_cur 是否等于 _M_finish._M_last。如果相等,说明当前数据块已满。
- 如果数据块未满:新元素直接添加到 _M_finish._M_cur 指向的位置,然后将 _M_finish._M_cur 向后移动一位。
- 如果数据块已满:
    - deque 需要移动到下一个数据块。它通过 _M_finish._M_node 指针向后移动一位,找到 _M_map 中下一个可用的指针。
- 如果 _M_map 中有空闲块,就直接使用。如果没有,deque 会分配一个新块。
- 然后,将新元素插入到新块的开头,并更新 _M_finish 迭代器的 _M_node 和 _M_cur 指针。
 
当_M_map满了后,会对其扩容。
头部插入与尾部插入类似,但操作的是_M_start,并且头部插入是从后往前插入。
删除元素
以尾部删除为例:
- 首先,将 _M_finish._M_cur 指针向前移动一位,使其指向最后一个元素。
- 如果 _M_finish._M_cur 现在等于 _M_finish._M_first,这意味着当前数据块中的元素已被全部删除。
- 在这种情况下,deque 会释放这个数据块,并将 _M_finish 迭代器的 _M_node 指针向前移动,指向 _M_map 中的前一个数据块。
随机访问
对于dq[i]:
- 
    计算 _M_start 迭代器的块内偏移 start_offset_in_block = _M_start._M_cur - _M_start._M_first
- 
    计算i相对deque开头的总偏移量 total_offset = i + start_offset_in_block
- 
    计算数据块索引 block_index = total_offset / map_size
- 
    定位数据块的指针 target_map_node = _M_start._M_node + block_index
- 
    计算块内偏移 offset_in_block = total_offset % block_size
- 
    定位元素的指针 target_element_ptr = *target_map_node + offset_in_block
deque的size
deque对象大小是固定的。
std::deque<int> dq;
std::cout << sizeof(dq) << std::endl; // 输出80
deque的构造函数
deque的构造函数和vector类似:
deque<int> v1;                                  // 无元素
deque<int> v2 {1, 2, 3, 4, 5};                  // 列表初始化
deque<int> v3(4);                               // 4个元素,初始值为0
deque<int> v4(5, 3);                            // 5个元素,初始值为3
deque<int> v5(v4);                              // 使用另外一个vector拷贝构造
deque<int> v6(std::move(v5));                   // 使用另外一个vector移动构造
deque<int> v7(arr1, arr1+5);                    // 使用指针初始化
deque<int> v8(v4.begin(), v4.end());            // 使用迭代器初始化[左闭,右开)
deque的成员函数
访问
| 成员函数 | 函数说明 | 
|---|---|
| at(size_type n) | 带有边界检查,返回对索引 n处元素的引用。如果索引越界,则抛出std::out_of_range异常。 | 
| operator[](size_type n) | 返回对索引 n处元素的引用。不执行边界检查,速度更快。 | 
| front() | 返回对第一个元素的引用。 | 
| back() | 返回对最后一个元素的引用。 | 
修改
| 成员函数 | 说明 | 
|---|---|
| assign(size_type count, const T& value) | 将 deque的内容替换为count个值为value的元素。 | 
| push_front(const T& value) | 在 deque的开头添加一个新元素value。 | 
| push_back(const T& value) | 在 deque的末尾添加一个新元素value。 | 
| pop_front() | 移除 deque的第一个元素。 | 
| pop_back() | 移除 deque的最后一个元素。 | 
| insert(iterator position, const T& value) | 在 position前插入一个值为value的新元素。 | 
| erase(const_iterator position) | 移除 position处的元素。 | 
| emplace_front(Args&&... args) | 在 deque开头通过就地构造插入新元素。 | 
| emplace_back(Args&&... args) | 在 deque末尾通过就地构造插入新元素。 | 
| emplace(const_iterator position, Args&&... args) | 在 position处通过就地构造插入新元素。 | 
| clear() | 移除所有元素,使 deque变为空。 | 
| resize(size_type count) | 改变 deque的大小为count。 | 
| swap(deque& other) | 与另一个 deque交换内容。 | 
容量
| 成员函数 | 说明 | 
|---|---|
| empty() | 如果 deque中没有元素,则返回true。 | 
| size() | 返回 deque中元素的数量。 | 
| max_size() | 返回 deque可以容纳的最大元素数量。和系统或库实现相关。 | 
| shrink_to_fit() | 减少容量以适应其大小。可能会释放未使用的内存。非强制,能否达成依赖于实现。 | 
迭代器
| 成员函数 | 说明 | 
|---|---|
| begin() | 返回指向 deque第一个元素的迭代器。 | 
| end() | 返回指向 deque最后一个元素之后位置的迭代器。 | 
| rbegin() | 返回指向 deque最后一个元素的反向迭代器。 | 
| rend() | 返回指向 deque第一个元素之前位置的反向迭代器。 | 
| cbegin() | 返回指向 deque第一个元素的const迭代器。 | 
| cend() | 返回指向 deque最后一个元素之后位置的const迭代器。 | 
| crbegin() | 返回指向 deque最后一个元素的const反向迭代器。 | 
| crend() | 返回指向 deque第一个元素之前位置的const逆向迭代器。 |