• STL源码剖析(deque)


    deque是一个双向开口的容器,在头尾两端进行元素的插入跟删除操作都有理想的时间复杂度。

    deque使用的是分段连续线性空间,它维护一个指针数组(T** map),其中每个指针指向一块连续线性空间。

    (map左右两边一般留有剩余空间,用于前后插入元素,具体下面可以看到其实现)

    根据上图,可以了解到deque的迭代器的基本定义。

     1 template <class T, class Ref, class Ptr, size_t BufSiz>
     2 struct __deque_iterator {
     3     // 基本型别的定义
     4     typedef __deque_iterator<T, T&, T*, BufSiz>             iterator;
     5     typedef random_access_iterator_tag iterator_category;
     6     typedef T value_type;
     7     typedef Ptr pointer;
     8     typedef Ref reference;
     9     typedef size_t size_type;
    10     typedef ptrdiff_t difference_type;
    11     typedef T** map_pointer;
    12     typedef __deque_iterator self;
    13 
    14     // 缓冲区的大小
    15     tatic size_t buffer_size() { ... }    
    16 
    17     // 主要维护的三个指针
    18     T* cur;    // 指向当前元素
    19     T* first;   // 指向当前缓冲区的头
    20     T* last;   // 指向当前缓冲区的尾
    21 
    22     map_pointer node; // 指向当前缓冲区在map中的位置
    23     // ...
    24 };
    View Code

    deque的实现基本都是依赖于其迭代器的实现(主要是各种操作符的重载)

     1 // 用于跳到下一个缓冲区
     2 void set_node(map_pointer new_node) {
     3     node = new_node;
     4     first = *new_node;
     5     last = first + difference_type(buffer_size());
     6 }
     7 
     8 reference operator*() const { return *cur; }
     9 pointer operator->() const { return &(operator*()); }
    10 
    11 self& operator++() {
    12     ++cur;
    13     if (cur == last) {  // 到达缓冲区尾端
    14         set_node(node + 1);
    15         cur = first;
    16     }
    17     return *this; 
    18 }
    19 
    20 self& operator--() {
    21     if (cur == first) { // 已到达缓冲区头端
    22         set_node(node - 1);
    23         cur = last;
    24     }
    25     --cur;
    26     return *this;
    27 }
    28 
    29 // 迭代器之间的距离(相隔多少个元素)
    30 difference_type operator-(const self& x) const {
    31     return difference_type(buffer_size()) * (node - x.node - 1) +
    32       (cur - first) + (x.last - x.cur);
    33 }
    View Code

    该迭代器还重载了operator+=、operator+、operator-=、operator-(difference_type)等,

    都是通过set_node()跟调整cur、first、last、node成员来实现。同时重载的operator[]使用operator+来进行随机存取。

     1 self& operator+=(difference_type n) {
     2     difference_type offset = n + (cur - first);
     3     if (offset >= 0 && offset < difference_type(buffer_size()))
     4         cur += n;
     5     else {
     6         // 目标在不同的缓冲区
     7         difference_type node_offset =
     8         offset > 0 ? offset / difference_type(buffer_size())
     9                    : -difference_type((-offset - 1) / buffer_size()) - 1;
    10         // 跳到相应的缓冲区
    11         set_node(node + node_offset);
    12         // 调整cur指针
    13         cur = first + (offset - node_offset * difference_type(buffer_size()));
    14     }
    15     return *this;
    16 }
    17 
    18 // 下面的都直接或间接的调用operator+=
    19 self operator+(difference_type n) const {
    20     self tmp = *this;
    21     return tmp += n;
    22 }
    23 
    24 self& operator-=(difference_type n) { return *this += -n; }
    25  
    26 self operator-(difference_type n) const {
    27     self tmp = *this;
    28     return tmp -= n;
    29 }
    30 
    31 reference operator[](difference_type n) const { return *(*this + n); }    
    View Code

    有了__deque_iterator,deque的基本实现就比较简单了(主要维护start、finish这两个迭代器)

    下面是deque的基本定义

     1 template <class T, class Alloc = alloc, size_t BufSiz = 0> 
     2 class deque {
     3 public:                         
     4     typedef T value_type;
     5     typedef value_type* pointer;
     6     typedef size_t size_type;
     7     typedef pointer* map_pointer;
     8 public:
     9     typedef __deque_iterator<T, T&, T*, BufSiz>  iterator;
    10 protected:
    11     iterator start;    // 第一个节点
    12     iterator finish;   // 最后一个结点
    13 
    14     map_pointer map;
    15     size_type map_size;  
    16 public:
    17     iterator begin() { return start; }
    18     iterator end() { return finish; }
    19     
    20     reference operator[](size_type n) { return start[difference_type(n)]; } // 调用迭代器重载的operator[]
    21    
    22     // ...
    23 }
    View Code

    deque的constructor会调用create_map_and_nodes()来初始化map

     1 // 每次配置一个元素大小的配置器
     2 typedef simple_alloc<value_type, Alloc> data_allocator;
     3 // 每次配置一个指针大小的配置器
     4 typedef simple_alloc<pointer, Alloc> map_allocator;
     5 
     6 template <class T, class Alloc, size_t BufSize>
     7 void deque<T, Alloc, BufSize>::create_map_and_nodes(size_type num_elements) {
     8     // 需要分配的结点数  如果为能整除 则多分配多一个结点
     9     size_type num_nodes = num_elements / buffer_size() + 1;
    10 
    11     // 分配结点内存 (前后预留一个 用于扩充)
    12     map_size = max(initial_map_size(), num_nodes + 2);
    13     map = map_allocator::allocate(map_size);
    14 
    15     // 将需要分配缓冲区的结点放在map的中间
    16     map_pointer nstart = map + (map_size - num_nodes) / 2;
    17     map_pointer nfinish = nstart + num_nodes - 1;
    18     
    19     map_pointer cur;
    20     // 为了简化 去掉了异常处理的代码
    21     for (cur = nstart; cur <= nfinish; ++cur)
    22         *cur = allocate_node(); // 为每个结点分配缓冲区
    23     }
    24 
    25     // 设置start、finish指针
    26     start.set_node(nstart);
    27     finish.set_node(nfinish);
    28     start.cur = start.first;
    29     finish.cur = finish.first + num_elements % buffer_size();
    30 }
    View Code

    下面就剩下插入跟删除元素的实现了,首先看看关于push_front()的操作的实现。

     1 void push_front(const value_type& t) {
     2     if (start.cur != start.first) {    // 第一缓冲区还有容量
     3         construct(start.cur - 1, t);  
     4         --start.cur;
     5     }
     6     else
     7         push_front_aux(t);
     8   }
     9 
    10 // 如果第一缓冲区容量不足会调用这个函数来配置新的缓冲区
    11 template <class T, class Alloc, size_t BufSize>
    12 void deque<T, Alloc, BufSize>::push_front_aux(const value_type& t) {
    13     value_type t_copy = t;
    14     reserve_map_at_front();   // 可能导致map的重新整治
    15     *(start.node - 1) = allocate_node();
    16     start.set_node(start.node - 1);
    17     start.cur = start.last - 1;
    18     construct(start.cur, t_copy);
    19 } 
    20 
    21 // 根据map前面为分配的结点数量来判断是否需要重新整治
    22 void reserve_map_at_front (size_type nodes_to_add = 1) {
    23     if (nodes_to_add > start.node - map)
    24         reallocate_map(nodes_to_add, true);
    25 }
    View Code

    上面留下的reallocate_map函数执行如下功能:

    1.如果map中空闲指针足够多,则将已分配的结点移到map的中间。

    2.否则重新分配一个map,将旧的map释放,把已分配的结点移到new_map的中间。

    然后调整start跟finish迭代器。

    然后是pop_front()的实现

     1 void pop_front() {
     2     if (start.cur != start.last - 1) { 
     3         destroy(start.cur);
     4         ++start.cur;
     5     }
     6     else 
     7         pop_front_aux();
     8 }
     9 
    10 // 当前缓冲区只剩一个元素
    11 template <class T, class Alloc, size_t BufSize>
    12 void deque<T, Alloc, BufSize>::pop_front_aux() {
    13     destroy(start.cur);
    14     deallocate_node(start.first);  // 释放该缓冲区
    15     start.set_node(start.node + 1);
    16     start.cur = start.first;
    17 }      
    View Code

    而push_back()跟pop_back()的实现跟上面的大同小异。

    最后看看erase()跟insert()的实现

     1 iterator erase(iterator pos) {
     2     iterator next = pos;
     3     ++next;
     4     difference_type index = pos - start; // 迭代器的operator-
     5     if (index < (size() >> 1)) { // 如果清除点之前的元素比较少
     6         // 将清除点之前的所有元素后移一位  然后删除第一个元素
     7         copy_backward(start, pos, next);  // 利用了迭代器的operator--
     8         pop_front();
     9     }
    10     else { // 如果清除点之后的元素比较少
    11         // 将清除点之后的所有元素前移一位  然后删除最后一个元素
    12         copy(next, finish, pos);  // 利用了迭代器的operator++
    13         pop_back();
    14     }
    15     return start + index;
    16 }
    View Code
     1 iterator insert(iterator position, const value_type& x) {
     2     if (position.cur == start.cur) {
     3         // 插入位置为begin()
     4         push_front(x);
     5         return start;
     6     }
     7     else if (position.cur == finish.cur) {
     8         // 插入位置为end()
     9         push_back(x);
    10         iterator tmp = finish;
    11         --tmp;
    12         return tmp;
    13     }
    14     else {
    15         // 如果插入位置是在(begin(), end())
    16         return insert_aux(position, x);
    17     }
    18 }
    19 
    20 // insert_aux()跟erase()实现类似
    21 // 调用copy()或者copy_backward()将元素前移或者后移
    22 // 然后修改原来位置的值
    View Code
  • 相关阅读:
    1. 两数之和
    RabbitMQ主题交换机
    RabbitMQ路由
    RabbitMQ发布订阅
    RabbitMQ简介和安装
    55. 跳跃游戏
    63. 不同路径 II
    62. 不同路径
    6. Z 字形变换
    sql注入攻击与防御第一章(笔记)
  • 原文地址:https://www.cnblogs.com/runnyu/p/6000368.html
Copyright © 2020-2023  润新知