STL源码剖析:序列式容器

前言

  • 容器,置物之所也。就是存放数据的地方。

  • array(数组)、list(串行)、tree(树)、stack(堆栈)、queue(队列)、hash table(杂凑表)、set(集合)、map(映像表)…等等。

  • 容器按照存储的方式可以分为:序列式容器和关联式容器

    • 序列式容器:容器里面的数据可以进行排序,但是不会自动排序,可以使用算法进行排序

    • 关联式容器:容器里面的数据不可排序,以键值对的形式存储

Vector

  • 和数组类似,区别是数组大小是固定,Vector可以动态改变,不用但是越界的问题

  • 动态大小的实现:

    • 当插入数据时,vector检查到数据满了,就会重新申请一块更大的内存,然后将原来的数据拷贝到新申请的内存中,并释放原来的内存

    • 每次重新申请内存时,申请的大小是原来的两倍,以避免多次申请

  • vector声明:

template <class T, class Alloc = alloc>
class vector
{
public:
    // 类型相关定义
    typedef T value_type;
    typedef value_type* pointer;
    typedef value_type* iterator;
    typedef value_type& reference;
    typedef size_t size_type;
    typedef ptrdiff_t difference_type;

protected:
    //定义配置器
    typedef simple_alloc<value_type, Alloc> data_allocator;
    
    iterator start;             // 内存起始地址
    iterator finish;            // 当时使用内存的末尾地址,每次插入和删除都会修改
    iterator end_of_storage;     // 内存的结束地址
    
    // 关键函数,在某个位置插入一个数据
    void insert_aux(iterator position, const T& x);
    
    // 使用配置器释放内存
    void deallocate() 
    {
        if (start)
            data_allocator::deallocate(start, end_of_storage - start);
    }
    
    // 申请并初始化一块大小为n的内存,并初始化为value
    void fill_initialize(size_type n, const T& value) 
    {
        start = allocate_and_fill(n, value);
        finish = start + n;
        end_of_storage = finish;
    }

public:
    // 迭代器起始位置
    iterator begin() { return start; }
    // 迭代器结束位置
    iterator end() { return finish; }
    // 容器大小,即真实的数据个数
    size_type size() const { return size_type(end() - begin()); }
    // 容器容量,即申请的内存大小
    size_type capacity() const { return size_type(end_of_storage - begin()); }
    // 容器是否为空
    bool empty() const { return begin() == end(); }
    // 重载[]运算符,取出对应position的数据,下标从0开始
    reference operator[](size_type n) { return *(begin() + n); }
    // 构造函数
    vector() : start(0), finish(0), end_of_storage(0) {}
    
    vector(size_type n, const T& value) { fill_initialize(n, value); }
    
    vector(int n, const T& value) { fill_initialize(n, value); }
    
    vector(long n, const T& value) { fill_initialize(n, value); }
    
    explicit vector(size_type n) { fill_initialize(n, T()); }
    // 析构函数
    ~vector()
    {
        destroy(start, finish);
        deallocate();
    }
    // 取出起始数据
    reference front() { return *begin(); }
    // 取出末尾数据
    reference back() { return *(end() - 1); }
    // 从尾部插入一个数据
    void push_back(const T& x) 
    {
        if (finish != end_of_storage)
        {
            // 内存没有满,直接插入
            construct(finish, x);
            ++finish;
        }
        else
        {
            // 内存满了,需要扩容内存,然后插入数据
            insert_aux(end(), x);
        }
    }
    
    // 弹出最后一个数据
    void pop_back() 
    {
        --finish;
        destroy(finish);
    }
    
    // 删除时,将后面的数据覆盖前面的数据,然后释放最后一个数据;如果删除的数据是最后一个数据,那么直接
    // 释放即可
    iterator erase(iterator position)
    {
        if (position + 1 != end())
            // 将position + 1到finish的数据,拷贝到position开始的地方
            copy(position + 1, finish, position);
        
        --finish;
        destroy(finish);
        return position;
    }
    
    // 修改vector的大小,新的size比老的size小,直接删除多余的数据;新的size比老的size大,直接插入
    void resize(size_type new_size, const T& x)
    {
        if (new_size < size())
            erase(begin() + new_size, end());
        else
            insert(end(), new_size - size(), x);
    }
    // 外部统一调用接口,一层封装
    void resize(size_type new_size) { resize(new_size, T()); }
    // 删除容器中所有数据,不会释放内存
    void clear() { erase(begin(), end()); }
    
protected:
    // 申请并初始化一块内存
    iterator allocate_and_fill(size_type n, const T& x) 
    {
        iterator result = data_allocator::allocate(n);
        uninitialized_fill_n(result, n, x); 
        return result;
    }
}
Vector迭代器
  • Vector维护的是一个线性空间,无论里面元素类型是什么,普通指针都可以作为迭代器,满足统一接口的要求

  • 迭代器需要支持的方法:

    • operator*

    • operator->

    • operator++

    • operator--

    • operator+

    • operator-

    • operator+=

    • operator-=

  • 上述方法原生指针都支持

  • 迭代器类型是:Random Access Iterator

erase方法
// 清除[first,last)的所有元素
iterator erase(iterator first, iterator last) 
{
    // 拷贝[last,finish)到[first,first+(finish-last)),返回值是first+(finish-last)
    iterator i = copy(last, finish, first); 
    // 析构尾部多余的数据
    destroy(i, finish);
    // 调整finish指针
    finish = finish - (last - first);
    return first;
}

// 清除某个位置的元素,可以理解为erase(iterator first, iterator last)函数中last=first+1
iterator erase(iterator position) 
{
    if (position + 1 != end())
        copy(position + 1, finish, position); 
    --finish;
    destroy(finish); 
    return position;
}
insert方法
  • 代码解析:

// 从position开始,插入n个元素,元素初值为x
template <class T, class Alloc>
void vector<T, Alloc>::insert(iterator position, size_type n, const T& x)
{
    // 插入元素的个数为0时,直接返回不处理
    if (n != 0)
    { 
        // 剩余的空间大于插入的个数
        if (size_type(end_of_storage - finish) >= n)
        {
            T x_copy = x;
            const size_type elems_after = finish - position;
            iterator old_finish = finish;
            
            if (elems_after > n)
            {
                 // 插入点到finish的个数大于插入数据的个数
                uninitialized_copy(finish - n, finish, finish);
                finish += n;
                copy_backward(position, old_finish - n, old_finish);
                fill(position, position + n, x_copy); 
            }
            else 
            {
                // 插入点到finish的个数小于插入数据的个数,需要先在尾部插入,使得插入点到finish的个数                  // 等于插入数据的个数,然后和if分支的处理相同
                uninitialized_fill_n(finish, n - elems_after, x_copy);
                finish += n - elems_after;
                uninitialized_copy(position, old_finish, finish);
                finish += elems_after;
                fill(position, old_finish, x_copy);
            }
        }
        // 剩余空间不够,直接重新申请内存
        else 
        {
            const size_type old_size = size();
            const size_type len = old_size + max(old_size, n);

            iterator new_start = data_allocator::allocate(len);
            iterator new_finish = new_start;
            
            // 以下操作可能失败,所以try一把
            __STL_TRY {
                new_finish = uninitialized_copy(start, position, new_start);
                new_finish = uninitialized_fill_n(new_finish, n, x);
                new_finish = uninitialized_copy(position, finish, new_finish);
            }
            // 定义了__STL_USE_EXCEPTIONS宏就catch,不定义就直接抛异常
            # ifdef __STL_USE_EXCEPTIONS
            catch(...) {
                destroy(new_start, new_finish);
                data_allocator::deallocate(new_start, len);
                throw;
            }
            # endif 
            // 释放原来内存,修改3个指针
            destroy(start, finish);
            deallocate();
            start = new_start;
            finish = new_finish;
            end_of_storage = new_start + len;
        }
    }
}
图解
  • 剩余的空间大于插入的个数

  • 插入点到finish的个数小于插入数据的个数

  • 剩余空间不够,直接重新申请内存

常用方法
  • front

  • back

  • push_back

  • pop_back

  • erase

  • clear

  • size

  • insert:不建议使用,效率低

List

特点
  • 一个双向链表

  • 一个成环的双向列表

  • 一个有一个门卫节点(空节点)的双向列表

节点定义
template <class T>
struct __list_node 
{
    typedef void* void_pointer;
    void_pointer prev; // 指向前一个节点
    void_pointer next; // 指向后一个节点
    T data; // 存储的数据
};
迭代器定义
  • 链表的迭代器类型是bidirectional_itrtator

  • 内部就是一个__list_node<T>的数据,重载一些操作符,满足STL标准

  • 以下代码中包含了迭代器需要重载的所有方法

template<class T, class Ref, class Ptr>
struct __list_iterator 
{
    typedef __list_iterator<T, T&, T*> iterator;
    typedef __list_iterator<T, Ref, Ptr> self;

    typedef bidirectional_iterator_tag iterator_category;
    typedef T value_type;
    typedef Ptr pointer;
    typedef Ref reference;
    typedef __list_node<T>* link_type;
    typedef size_t size_type;
    typedef ptrdiff_t difference_type;

    link_type node; // 迭代器内部的核心数据,指向链表中的某一个节点

    // 构造函数
    __list_iterator(link_type x) : node(x) {}
    
    __list_iterator() {}
    
    __list_iterator(const iterator& x) : node(x.node) {}
    
    bool operator==(const self& x) const { return node == x.node; }
    
    bool operator!=(const self& x) const { return node != x.node; }
    
    reference operator*() const { return (*node).data; }
    
    pointer operator->() const { return &(operator*()); }

    // 迭代器++就是下一个节点
    self& operator++()
    {
        node = (link_type)((*node).next);
        return *this;
    }
    
    self operator++(int)
    {
        self tmp = *this;
        ++*this;
        return tmp;
    }

    // 迭代器--就是上一个节点
    self& operator--()
    {
        node = (link_type)((*node).prev);
        return *this;
    }
    
    self operator--(int)
    {
        self tmp = *this;
        --*this;
        return tmp;
    }
};
list定义
  • list不仅是一个双向串行,而且还是一个环状双向串行。所以它只需要一个数据,便可以完整表现整个链表

template <class T, class Alloc = alloc> 
class list 
{
protected:
    typedef __list_node<T> list_node;
    
public:
    typedef list_node* link_type;
    
protected:
    link_type node; // 核心数据,表示整个链表
    ...
};
  • 门卫节点:门卫节点就是一个数据为空的节点,其余和普通节点完全相同

    • 门卫节点在链表初始化的创建

    • 在节点为空时,门卫节点的prev和next都指向自己

    • 门卫节点的下一个节点是头结点,自身是尾节点

  • 说明:节点的插入指的是在某节点之前插入

list的相关方法
// 申请一个节点
link_type get_node() { return list_node_allocator::allocate(); }

// 释放一个节点
void put_node(link_type p) { list_node_allocator::deallocate(p); }

// 创建一个节点并初始化
link_type create_node(const T& x) 
{
    link_type p = get_node();
    construct(&p->data, x); // 
    return p;
}

// 删除一个节点
void destroy_node(link_type p) 
{
    destroy(&p->data); // 
    put_node(p);
}

// 无参构造函数
list() { empty_initialize(); }

void empty_initialize()
{
    node = get_node(); 
    node->next = node; 
    node->prev = node;
}

// 返回头结点
iterator begin() { return (link_type)((*node).next); }

// 放回尾节点
iterator end() { return node; }

// 判断链表是否为空
bool empty() const { return node->next == node; }

// 计算链表的大小
size_type size() const 
{
    size_type result = 0;
    distance(begin(), end(), result);
    return result;
}

// 返回头文件的数据
reference front() { return *begin(); }

// 返回尾节点的数据
reference back() { return *(--end()); }

// 尾部插入数据
void push_back(const T& x) { insert(end(), x); }

// 头部插入数据
void push_front(const T& x) {insert(begin(), x);}

// 删除头部节点
void pop_front() { erase(begin()); }

// 删除尾节点
void pop_back()
{
    iterator tmp = end();
    erase(--tmp);
}

// 在position前面插入一个节点,数据为x
iterator insert(iterator position, const T& x)
{
    link_type tmp = create_node(x); 

    tmp->next = position.node;
    tmp->prev = position.node->prev;
    (link_type(position.node->prev))->next = tmp;
    position.node->prev = tmp;
    
    return tmp;
}

// 删除指定位置的节点
iterator erase(iterator position) 
{
    link_type next_node = link_type(position.node->next);
    link_type prev_node = link_type(position.node->prev);
    prev_node->next = next_node;
    next_node->prev = prev_node;
    destroy_node(position.node);
    return iterator(next_node);
}

// 清空链表
template <class T, class Alloc>
void list<T, Alloc>::clear()
{
    link_type cur = (link_type) node->next; 
    while (cur != node) 
    { 
        link_type tmp = cur;
        cur = (link_type) cur->next;
        destroy_node(tmp); 
    }
    
    node->next = node;
    node->prev = node;
}

// 将数据为value的节点全部删除
template <class T, class Alloc>
void list<T, Alloc>::remove(const T& value)
{
    iterator first = begin();
    iterator last = end();
    while (first != last)
    {
        iterator next = first;
        ++next;
        
        if (*first == value) 
            erase(first);
        
        first = next;
    }
}
常用方法
  • back

  • front

  • push_back

  • push_front

  • pop_front

  • pop_back

  • insert

  • erase

  • clear

  • size

deque

特点
  • 一种双向开口的连续线性空间

  • 与vector最大的区别是:deque可以两端插入和弹出,vector只能一端插入

  • deuqe底层维护的是一些分段定量的连续空间,对外提供一种按照连续线性空间的访问方法

  • deque内部唯一个BufSize大小的指针数组,数组中的每一个指针指向一块连续的内存,数据就存放在这些连续的内存中

  • deque的中有一个start迭代器和一个finish迭代器,分别指向数据头和数据尾

  • deuqe的数据维护如下图所示:

template <class T, class Alloc = alloc, size_t BufSiz = 0>
class deque {
public: 
    typedef T value_type;
    typedef value_type* pointer;
    ...
protected: 
    typedef pointer* map_pointer; // map_pointer本质就是T**,一个二重指针
    
protected: 
    ...
}
deque迭代器
  • deque迭代器类型是random_access_iterator_tag

  • 代码如下:

template <class T, class Ref, class Ptr, size_t BufSiz>
struct __deque_iterator 
{ 
    typedef __deque_iterator<T, T&, T*, BufSiz> iterator;
    typedef __deque_iterator<T, const T&, const T*, BufSiz> const_iterator;
    static size_t buffer_size() {return __deque_buf_size(BufSiz, sizeof(T)); }

    typedef random_access_iterator_tag iterator_category; // (1)
    typedef T value_type;
    typedef Ptr pointer;
    typedef Ref reference;
    typedef size_t size_type;
    typedef ptrdiff_t difference_type;
    typedef T** map_pointer;
    typedef __deque_iterator self;

    // 一下4个数据确定一个数据在deque中的位置
    T* cur;        // 指向连续内存中数据的当前位置
    T* first;    // 指向连续内存的起始位置
    T* last;    // 指向连续内存的结束位置

    map_pointer node; // 指向数组中的某个位置
...
};

// 计算每次申请的连续内存的大小
// n!=0表示大小由用户指定
inline size_t __deque_buf_size(size_t n, size_t sz)
{
    return n != 0 ? n : (sz < 512 ? size_t(512 / sz) : size_t(1));
}

// 将迭代器指向数组中一块行的连续空间,注意没有设置cur指针,所以需要单独指定
void set_node(map_pointer new_node) {
    node = new_node;
    first = *new_node;
    last = first + difference_type(buffer_size());
}

reference operator*() const { return *cur; }

pointer operator->() const { return &(operator*()); }

// 计算两个迭代器之间的距离
difference_type operator-(const self& x) const 
{
    return difference_type(buffer_size()) * (node - x.node - 1) + (cur - first) + (x.last - x.cur);
}

// 前置++

self& operator++() 
{
    ++cur;
    if (cur == last) 
    {
        set_node(node + 1);
        cur = first;
    }
    return *this;
}

// 后置++
self operator++(int) 
{
    self tmp = *this;
    ++*this;
    return tmp;
}

//前置--
self& operator--()
{
    if (cur == first) 
    {
        set_node(node - 1);
        cur = last; 
    }
    --cur;
    return *this;
}

// 后置--
self operator--(int) 
{
    self tmp = *this;
    --*this;
    return tmp;
}

// 迭代器跳跃n个距离
self& operator+=(difference_type n) 
{
    difference_type offset = n + (cur - first);
    if (offset >= 0 && offset < difference_type(buffer_size()))
    {
        cur += n;
    }
    else 
    {
        difference_type node_offset = offset > 0 ? offset / difference_type(buffer_size())
: -difference_type((-offset - 1) / buffer_size()) - 1;

    set_node(node + node_offset);
    cur = first + (offset - node_offset * difference_type(buffer_size()));
    }
    
    return *this;
}

// 迭代器跳跃n个距离
self operator+(difference_type n) const 
{
    self tmp = *this;
    return tmp += n; // 调用operator+=()方法
}

// 迭代器跳跃n个距离,方向是反方向
self& operator-=(difference_type n) 
{ 
    return *this += -n;
}

self operator-(difference_type n) const
{
    self tmp = *this;
    return tmp -= n; 
}

// 重载operator[],内部调用operator+=()方法
reference operator[](difference_type n) const 
{ 
    return *(*this + n); 
}

bool operator==(const self& x) const { return cur == x.cur; }

bool operator!=(const self& x) const { return !(*this == x); }

bool operator<(const self& x) const 
{
    return (node == x.node) ? (cur < x.cur) : (node < x.node);
}
deque定义
  • 内部一个map_pointer指向一个指针数组

  • start迭代器指向数据的起始位置

  • finish迭代器指向数据的结束位置

  • 代码如下:

template <class T, class Alloc = alloc, size_t BufSiz = 0>
class deque 
{
public: 
    typedef T value_type;
    typedef value_type* pointer;
    typedef size_t size_type;

public: 
    typedef __deque_iterator<T, T&, T*, BufSiz> iterator;
    
protected: 
    typedef pointer* map_pointer;
    
protected:
    iterator start;
    iterator finish;
    map_pointer map;

    size_type map_size; 
    ...
};

iterator begin() 
{ 
    return start;
}

iterator end() 
{
    return finish; 
}

reference operator[](size_type n)
{
    // 内部调用迭代器的重载[]函数
    return start[difference_type(n)]; 
}

reference front() 
{ 
    return *start; 
} 

reference back() 
{
    iterator tmp = finish;
    --tmp; 
    return *tmp; 
}

size_type size() const { return finish - start; }

type max_size() const { return size_type(-1); }

bool empty() const { return finish == start; }
  • deque初始化代码如下:
deque(int n, const value_type& value) : start(), finish(), map(0), map_size(0)
{
    fill_initialize(n, value);
}

template <class T, class Alloc, size_t BufSize>
void deque<T, Alloc, BufSize>::fill_initialize(size_type n, const value_type& value) 
{
    create_map_and_nodes(n); 
    map_pointer cur;
    __STL_TRY 
    {
        for(cur = start.node; cur < finish.node; ++cur)
        {
            //为每个节点设置连续内存块
            uninitialized_fill(*cur, *cur + buffer_size(), value);
        }
        // 为最后一个节点设置连续内存块,可能还有备用空间,因此分别处理
        uninitialized_fill(finish.first, finish.cur, value);
    }
    catch(...) 
    {
        ...
    }
}

template <class T, class Alloc, size_t BufSize>
void deque<T, Alloc, BufSize>::create_map_and_nodes(size_type num_elements)
{
    size_type num_nodes = num_elements / buffer_size() + 1;
    map_size = max(initial_map_size(), num_nodes + 2);
    map = map_allocator::allocate(map_size);

    map_pointer nstart = map + (map_size - num_nodes) / 2;
    map_pointer nfinish = nstart + num_nodes - 1;
    map_pointer cur;
    
    __STL_TRY 
    {
        // 为每一个节点申请内存
        for(cur = nstart; cur <= nfinish; ++cur)
            *cur = allocate_node();
    }
    catch(...) 
    {
        ...
    }

    start.set_node(nstart);
    finish.set_node(nfinish);
    start.cur = start.first;
    finish.cur = finish.first + num_elements % buffer_size();
}
push_back, push_front代码
void push_back(const value_type& t) {
    if (finish.cur != finish.last - 1)
    {
        construct(finish.cur, t);
        ++finish.cur; 
    }
    else 
    {
        push_back_aux(t);
    }
}

template <class T, class Alloc, size_t BufSize>
void deque<T, Alloc, BufSize>::push_back_aux(const value_type& t)
{
    value_type t_copy = t;
    reserve_map_at_back(); // 关键函数:修改数组大小并拷贝数据
    *(finish.node + 1) = allocate_node();
    
    __STL_TRY 
    {
        construct(finish.cur, t_copy);
        finish.set_node(finish.node + 1);
        finish.cur = finish.first;
    }
    __STL_UNWIND(deallocate_node(*(finish.node + 1)));
}

void push_front(const value_type& t) 
{
    if(start.cur != start.first) 
    { 
        construct(start.cur - 1, t); 
        --start.cur; 
    }
    else 
    {
        push_front_aux(t);
    }
}

template <class T, class Alloc, size_t BufSize>
void deque<T, Alloc, BufSize>::push_front_aux(const value_type& t)
{
    value_type t_copy = t;
    reserve_map_at_front(); // 关键函数:修改数组大小并拷贝数据
    *(start.node - 1) = allocate_node();
    
    __STL_TRY 
    {
        start.set_node(start.node - 1);
        start.cur = start.last - 1;
        construct(start.cur, t_copy);
    }
    catch(...) 
    {
        start.set_node(start.node + 1);
        start.cur = start.first;
        deallocate_node(*(start.node - 1));
        throw;
    }
}

void reserve_map_at_back (size_type nodes_to_add = 1) 
{
    if(nodes_to_add + 1 > map_size - (finish.node - map))
        reallocate_map(nodes_to_add, false);
}

void reserve_map_at_front (size_type nodes_to_add = 1) 
{
    if(nodes_to_add > start.node - map)
        reallocate_map(nodes_to_add, true);
}

// 类似vector扩容,共3步,只移动数组中的数据,指针指针指向的内存片段里面的数据不动
// 1. 申请一块行的内存
// 2. 将原来的数据拷贝到新的内存中
// 3. 释放原来的内存
template <class T, class Alloc, size_t BufSize>
void deque<T, Alloc, BufSize>::reallocate_map(size_type nodes_to_add, bool add_at_front)
{
    size_type old_num_nodes = finish.node - start.node + 1;
    size_type new_num_nodes = old_num_nodes + nodes_to_add;
    map_pointer new_nstart;
    
    // 数组足够大,不用重新申请,直接拷贝
    if (map_size > 2 * new_num_nodes) 
    {
        new_nstart = map + (map_size - new_num_nodes) / 2 + (add_at_front ? nodes_to_add : 0);
        if (new_nstart < start.node)
        {
            copy(start.node, finish.node + 1, new_nstart);
        }
        else
            copy_backward(start.node, finish.node + 1, new_nstart + old_num_nodes);
        }
    else 
    {
        // 原来的数组不够,重新申请,拷贝,再释放
        size_type new_map_size = map_size + max(map_size, nodes_to_add) + 2;
        map_pointer new_map = map_allocator::allocate(new_map_size);
        new_nstart = new_map + (new_map_size - new_num_nodes) / 2 + (add_at_front ? nodes_to_add : 0);

        copy(start.node, finish.node + 1, new_nstart);
        map_allocator::deallocate(map, map_size);
        map = new_map;
        map_size = new_map_size;
     }

    start.set_node(new_nstart);
    finish.set_node(new_nstart + old_num_nodes - 1);
}
pop_back,pop_front代码
void pop_back()
{
    if(finish.cur != finish.first) 
    {
        --finish.cur; 
        destroy(finish.cur); 
    }
    else
    {
        pop_back_aux(); 
    }
}

// 跨行处理,释放内存,调整finish指针
template <class T, class Alloc, size_t BufSize>
void deque<T, Alloc, BufSize>::pop_back_aux() 
{
    deallocate_node(finish.first); 
    finish.set_node(finish.node - 1); 
    finish.cur = finish.last - 1;
    destroy(finish.cur);
}

void pop_front() 
{
    if(start.cur != start.last - 1) 
    {
        destroy(start.cur);
        ++start.cur;
    }
    else
    {
        pop_front_aux();
    }
}

// 跨行处理,释放内存,调整start指针
template <class T, class Alloc, size_t BufSize>
void deque<T, Alloc, BufSize>::pop_front_aux() 
{
    destroy(start.cur);
    deallocate_node(start.first);
    start.set_node(start.node + 1);
    start.cur = start.first;
}
clear代码
template <class T, class Alloc, size_t BufSize>
void deque<T, Alloc, BufSize>::clear() 
{
    // 释放中间数据存储满的内存块
    for (map_pointer node = start.node + 1; node < finish.node; ++node) 
    {
        destroy(*node, *node + buffer_size());
        data_allocator::deallocate(*node, buffer_size());
    }
    
    // start和finish不指向同一块内存
    if (start.node != finish.node) 
    {
        destroy(start.cur, start.last); // 析构start内存块中的数据
        destroy(finish.first, finish.cur); // 析构finish内存块中的数据
        data_allocator::deallocate(finish.first, buffer_size()); // 释放finish内存块,注意保留了start内存块的数据
    }
    else 
    {
         // start和finish指向同一块内存
        destroy(start.cur, finish.cur); 
    }
    
    // 调整状态
    finish = start; 
}
insert代码
iterator insert(iterator position, const value_type& x) 
{
    if(position.cur == start.cur) 
    {
        // 头插直接复用push_front函数
        push_front(x); 
        return start;
    }
    else if (position.cur == finish.cur)
    { 
        // 尾插直接复用push_front函数
        push_back(x); 
        iterator tmp = finish;
        --tmp;
        return tmp;
    }
    else 
    {
        // 其他情况调用insert_aux函数
           return insert_aux(position, x); 
    }
}

template <class T, class Alloc, size_t BufSize>
typename deque<T, Alloc, BufSize>::iterator
deque<T, Alloc, BufSize>::insert_aux(iterator pos, const value_type& x) 
{
    difference_type index = pos - start;
    value_type x_copy = x;

    if (index < size() / 2) 
    {
        // 插入点距离start指针近,在开头插入一个与头元素相同的数据,然后调用copy拷贝
        push_front(front());
        iterator front1 = start;
        ++front1;
        iterator front2 = front1;
        ++front2;
        pos = start + index;
        iterator pos1 = pos;
        ++pos1;
        copy(front2, pos1, front1);
    }
    else 
    {
        // 插入点距离finish指针近,在尾部插入一个与尾元素相同的数据,然后调用copy_backward拷贝
        push_back(back());
        iterator back1 = finish;
        --back1;
        iterator back2 = back1;
        --back2;
        pos = start + index;

        copy_backward(pos, back2, back1); 
    }
    
    *pos = x_copy; 
    return pos;
}

Stack

  • 底层使用的是deque

  • 允许在一个方向上插入、移除和查询

  • stack没有迭代器,不允许遍历

  • 代码如下:

template <class T, class Sequence = deque<T> >
class stack 
{
    
friend bool operator== __STL_NULL_TMPL_ARGS (const stack&, const stack&);  
    
friend bool operator< __STL_NULL_TMPL_ARGS (const stack&, const stack&);
    
public:
    typedef typename Sequence::value_type value_type;
    typedef typename Sequence::size_type size_type;
    typedef typename Sequence::reference reference;
    typedef typename Sequence::const_reference const_reference;
    
protected:
    Sequence c;  // 存放数据的容器
    
public:
    // stack是否为空
    bool empty() const 
    { 
        return c.empty(); 
    }
    
    // stack大小
    size_type size() const 
    { 
        return c.size(); 
    }
    
    // 查询栈顶数据
    reference top() 
    { 
        return c.back();
    }
    
    // 常函数查询栈顶数据
    const_reference top() const 
    {
        return c.back(); 
    }

    // 栈顶插入数据
    void push(const value_type& x) 
    { 
        c.push_back(x); 
    }
    
    // 栈顶弹出数据
    void pop() 
    { 
        c.pop_back();
    }
};

// 判断两个栈的Sequence是否是同一个
template <class T, class Sequence>
bool operator==(const stack<T, Sequence>& x, const stack<T, Sequence>& y)
{
    return x.c == y.c;
}

// 判断两个栈Sequence的大小
template <class T, class Sequence>
bool operator<(const stack<T, Sequence>& x, const stack<T, Sequence>& y)
{
    return x.c < y.c;
}

Queue

  • queue底层使用的是deque

  • 两端可查询,一端可插入,一端可移除的操作

  • queue没有迭代器

  • 代码如下:

template <class T, class Sequence = deque<T> >
class queue 
{
    friend bool operator== __STL_NULL_TMPL_ARGS (const queue& x, const queue& y);
    
    friend bool operator< __STL_NULL_TMPL_ARGS (const queue& x, const queue& y);
    
public:
    typedef typename Sequence::value_type value_type;
    typedef typename Sequence::size_type size_type;
    typedef typename Sequence::reference reference;
    typedef typename Sequence::const_reference const_reference;
    
protected:
    Sequence c; // 底层容器
    
public:
    
    // 队列是否为空
    bool empty() const 
    { 
        return c.empty(); 
    }
    
    // 队列大小
    size_type size() const 
    { 
        return c.size(); 
    }
    
    // 获取队头数据
    reference front() 
    { 
        return c.front();
    }
    
    // 常函数获取队头数据
    const_reference front() const 
    {
        return c.front();
    }
    
    // 获取队尾数据
    reference back()
    {
        return c.back(); 
    }
    
    // 常函数获取队尾数据
    const_reference back() const 
    {
        return c.back();
    }

    // 队尾插入数据
    void push(const value_type& x) 
    { 
        c.push_back(x); 
    }
    
    // 对头移除数据
    void pop() 
    { 
        c.pop_front(); 
    }
};

template <class T, class Sequence>
bool operator==(const queue<T, Sequence>& x, const queue<T, Sequence>& y)
{
    return x.c == y.c;
}

template <class T, class Sequence>
bool operator<(const queue<T, Sequence>& x, const queue<T, Sequence>& y)
{
    return x.c < y.c;
}

Heap

介绍
  • 二叉树

    • 二叉树是每个结点最多有两个子树的树结构

  • 完全二叉树

    • 完全二叉树是一种特殊的二叉树

    • 定义:在一棵二叉树中,除了最后一层外,其余每一层的节点数都是满的

  • 满二叉树

    • 满二叉树是一种特殊的完全二叉树

    • 定义:在一棵二叉树中,每一层的节点数都是满的

    • 堆是一种特殊的完全二叉树

    • 父节点的值大于(小于)所有子节点的值

  • 最大堆:

    • 最大堆是堆的一种

    • 父节点的值大于所有子节点的值

    • 根节点的值最大

  • 最小堆

    • 最小堆是堆的一种

    • 父节点的值小于所有子节点的值

    • 根节点的值最小

  • 堆的存储形式:

    • 节点存储,类似链表,每一个节点有两个指针,分别指向两个子节点

    • vector存储:类似数组,从上到下,从左到由,一次将堆中的数据保存vector中即可,具备以下性质

      • 前提:根节点下标是1

      • 节点i的左子节点是2i,右子节点是2i+1

      • 节点i的父节点是i/2

  • 堆的存储如图所示:

heap算法
  • 最大堆插入

    • 第一步:将需要插入的数据放在最下一层的最后一个位置

    • 第二步:将插入的节点和其父节点比较,如果比父节点大,就父子节点互换,一直向上比较,只到根节点或是父节点比子节点大

    • 下图是将50插入堆的流程:

  • 最大堆删除
    • 堆的删除只能是删除根节点,不能删除非根节点
    • 第一步:互换根节点和最下面一层最右边的节点

    • 第二步:删除最下面一层最右边的节点(原来的根节点)

    • 第三步:将根节点与较大的子节点互换,一次向下处理,知道叶子节点,或比左右两个子节点都大

    • 下图是将根节点68删除的流程:

push_heap方法
template <class RandomAccessIterator>
inline void push_heap(RandomAccessIterator first, RandomAccessIterator last) 
{
    // 注意:在调用此函数时,数据已插入到尾部
    __push_heap_aux(first, last, distance_type(first), value_type(first));
}

template <class RandomAccessIterator, class Distance, class T>
inline void __push_heap_aux(RandomAccessIterator first, RandomAccessIterator last, Distance*, T*) 
{
    __push_heap(first, Distance((last - first) - 1), Distance(0), T(*(last - 1)));
}

template <class RandomAccessIterator, class Distance, class T>
void __push_heap(RandomAccessIterator first, Distance holeIndex, Distance topIndex, T value) 
{
    Distance parent = (holeIndex - 1) / 2; // 找出父节点
    while (holeIndex > topIndex && *(first + parent) < value) 
    {
        // 没有到达根节点,且父节点小于子节点,互换
        *(first + holeIndex) = *(first + parent); 
        holeIndex = parent; 
        parent = (holeIndex - 1) / 2; 
    } 
    
    // 将value填入到最后的节点中
    *(first + holeIndex) = value; 
}
pop_heap方法
template <class RandomAccessIterator>
inline void pop_heap(RandomAccessIterator first, RandomAccessIterator last)
{
    __pop_heap_aux(first, last, value_type(first));
}

template <class RandomAccessIterator, class T>
inline void __pop_heap_aux(RandomAccessIterator first, RandomAccessIterator last, T*) 
{
    __pop_heap(first, last-1, last-1, T(*(last-1)), distance_type(first));
}

template <class RandomAccessIterator, class T, class Distance>
inline void __pop_heap(RandomAccessIterator first, RandomAccessIterator last, RandomAccessIterator result, T value, Distance*) 
{
    // 设置尾值为首值
    *result = *first; 
    __adjust_heap(first, Distance(0), Distance(last - first), value);
}

/*
下面方法的实现流程和前述有点不一致,也可以达到目标,但是比较麻烦
流程:
    1.保存尾节点的值
    2.将头结点的值复制到尾节点,此时头结点为空值
    3.头结点向下,将子节点中较大的节点上移,只到叶子节点
    4.将保存的尾节点值,放入最后的叶子节点
    5.将叶子节点当成新插入的节点进行处理,也就是调用__push_heap
*/
template <class RandomAccessIterator, class Distance, class T>
void __adjust_heap(RandomAccessIterator first, Distance holeIndex, Distance len, T value) {
    Distance topIndex = holeIndex;
    Distance secondChild = 2 * holeIndex + 2; 
    while (secondChild < len) 
    {
        if (*(first + secondChild) < *(first + (secondChild - 1)))
            secondChild--;

        *(first + holeIndex) = *(first + secondChild);
        holeIndex = secondChild;
           secondChild = 2 * (secondChild + 1);
    }
    
    if (secondChild == len) 
    { 
        *(first + holeIndex) = *(first + (secondChild - 1));
        holeIndex = secondChild - 1;
    }

    __push_heap(first, holeIndex, topIndex, value);
}
sort_heap算法
  • 从堆中取出的值,每次都是最大值或是最小值,如果依次取出堆中的值,只到堆为空,将取出的值依次排列,这就是堆排序

  • 源码如下:

template <class RandomAccessIterator>
void sort_heap(RandomAccessIterator first, RandomAccessIterator last) 
{
    // 核心:循环调用pop_heap方法
    while (last - first > 1)
        pop_heap(first, last--);
}
make_heap方法
  • 作用:用于将容器中的数据,按照堆的规则进行调整

  • 源码如下:

// 将 [first,last) 排列为heap。
template <class RandomAccessIterator>
inline void make_heap(RandomAccessIterator first, RandomAccessIterator last)
{
    __make_heap(first, last, value_type(first), distance_type(first));
}

template <class RandomAccessIterator, class T, class Distance>
void __make_heap(RandomAccessIterator first, RandomAccessIterator last, T*,
Distance*) 
{
    if (last - first < 2) 
        return; // 如果长度为0或1,不必重新排列
    
    Distance len = last - first;
    Distance parent = (len - 2)/2; // 最下面一层的节点(叶子节点)不需要处理
    while (true) 
    {
        // 对[first,last)中的数据,从倒数第二层开始,依次调用__adjust_heap进行调整
        __adjust_heap(first, parent, len, T(*(first + parent)));
        
        if(parent == 0) 
            return;
        parent--;
    }
}

Priority_queue

  • 底层以最大堆结构实现

  • 没有迭代器

  • 源码如下:

template <class T, class Sequence = vector<T>, class Compare = less<typename Sequence::value_type> >
class priority_queue 
{
public:
    typedef typename Sequence::value_type value_type;
    typedef typename Sequence::size_type size_type;
    typedef typename Sequence::reference reference;
    typedef typename Sequence::const_reference const_reference;
    
protected:
    Sequence c;        // 底层容器
    Compare comp;    // 元素大小比较标准

public:
    priority_queue() : c() {}
    
    explicit priority_queue(const Compare& x) : c(), comp(x) {}

    template <class InputIterator>
    priority_queue(InputIterator first, InputIterator last, const Compare& x) : c(first, last), comp(x) 
    { 
        make_heap(c.begin(), c.end(), comp); 
    }

    template <class InputIterator>
    priority_queue(InputIterator first, InputIterator last) : c(first, last) 
    {
        make_heap(c.begin(), c.end(), comp); 
    }

    bool empty() const 
    { 
        return c.empty(); 
    }

    size_type size() const 
    { 
        return c.size();
    }

    const_reference top() const 
    { 
        return c.front();
    }

    void push(const value_type& x) 
    {
        __STL_TRY
        {
            c.push_back(x);
            push_heap(c.begin(), c.end(), comp); // 泛型算法
        }
        __STL_UNWIND(c.clear());
    }

    void pop() 
    {
        __STL_TRY 
        {
            pop_heap(c.begin(), c.end(), comp); // 泛型算法
            c.pop_back();
        }
        __STL_UNWIND(c.clear());
    }
};

Slist

  • 单向列表

  • 不在标准STL的范围内,可以学习

  • 相比list,slist有如下特点:

    • slist的插入是在插入点之后插入,不是list的在插入点之前插入

    • slist消耗的空间更小,操作的速度更快

  • slist的迭代器是forward iterator迭代器

  • 类似list中设计,slist也有一个门卫节点,门卫节点的下一个节点就是链表的头节点

  • slist的结构设计如下所示:

  • 节点和迭代器代码如下:
// 节点定义
struct __slist_node_base
{
    __slist_node_base* next;
};

template <class T>
struct __slist_node : public __slist_node_base
{
    T data;
};

inline __slist_node_base* __slist_make_link(__slist_node_base* prev_node,__slist_node_base* new_node)
{
    new_node->next = prev_node->next;
    prev_node->next = new_node;
    return new_node;
}

inline size_t __slist_size(__slist_node_base* node)
{
    size_t result = 0;
    for ( ; node != 0; node = node->next)
        ++result;
    return result;
}
// 迭代器
struct __slist_iterator_base
{
    typedef size_t size_type;
    typedef ptrdiff_t difference_type;
    typedef forward_iterator_tag iterator_category; 
    
    __slist_node_base* node; // 核心数据
    
    __slist_iterator_base(__slist_node_base* x) : node(x) {}
    
    void incr() { node = node->next; } 
    
    bool operator==(const __slist_iterator_base& x) const
    {
        return node == x.node;
    }
    
    bool operator!=(const __slist_iterator_base& x) const 
    {
        return node != x.node;
    }
};

template <class T, class Ref, class Ptr>
struct __slist_iterator : public __slist_iterator_base
{
    typedef __slist_iterator<T, T&, T*> iterator;
    typedef __slist_iterator<T, const T&, const T*> const_iterator;
    typedef __slist_iterator<T, Ref, Ptr> self;
    typedef T value_type;
    typedef Ptr pointer;
    typedef Ref reference;
    typedef __slist_node<T> list_node; //核心数据

    __slist_iterator(list_node* x) : __slist_iterator_base(x) {}

    __slist_iterator() : __slist_iterator_base(0) {}
    
    __slist_iterator(const iterator& x) : __slist_iterator_base(x.node) {}
    
    reference operator*() const { return ((list_node*) node)->data; }
    
    pointer operator->() const { return &(operator*()); }
    
    self& operator++()
    {
        incr();
        return *this;
    }
    
    self operator++(int)
    {
        self tmp = *this;
        incr(); 
        return tmp;
    }
    
    // 没有operator--
};
  • slist定义代码如下:
template <class T, class Alloc = alloc>
class slist
{
public:
    typedef T value_type;
    typedef value_type* pointer;
    typedef const value_type* const_pointer;
    typedef value_type& reference;
    typedef const value_type& const_reference;
    typedef size_t size_type;
    typedef ptrdiff_t difference_type;
    typedef __slist_iterator<T, T&, T*> iterator;
    typedef __slist_iterator<T, const T&, const T*> const_iterator;
    
private:
    typedef __slist_node<T> list_node;
    typedef __slist_node_base list_node_base;
    typedef __slist_iterator_base iterator_base;
    typedef simple_alloc<list_node, Alloc> list_node_allocator;

    static list_node* create_node(const value_type& x) 
    {
        list_node* node = list_node_allocator::allocate();
        
        __STL_TRY 
        {
            // 配置空间
            construct(&node->data, x); // 建构元素
            node->next = 0;
        }
        __STL_UNWIND(list_node_allocator::deallocate(node));

        return node;
    }
    
    static void destroy_node(list_node* node) 
    {
        destroy(&node->data); // 将元素解构
        list_node_allocator::deallocate(node); // 释还空间
    }
    
private:
    list_node_base head; // 头部,门卫节点
    
public:
    slist() { head.next = 0; }
    
    ~slist() { clear(); }
    
public:
    iterator begin() { return iterator((list_node*)head.next); }
    
    iterator end() { return iterator(0); } // 需要注意,下面说明
    
    size_type size() const { return __slist_size(head.next); }
    
    bool empty() const { return head.next == 0; }

    void swap(slist& L)
    {
        list_node_base* tmp = head.next;
        head.next = L.head.next;
        L.head.next = tmp;
    }
    
public:
    // 取头部元素
    reference front() { return ((list_node*) head.next)->data; }

    void push_front(const value_type& x) 
    {
        __slist_make_link(&head, create_node(x));
    }

    void pop_front() 
    {
        list_node* node = (list_node*) head.next;
        head.next = node->next;
        destroy_node(node);
    }
    ...
};
  • end()方法说明:
    • 表示链表的尾部

    • end()方法返回的是iterator(0)

    • 插入节点时,节点的next指针的值是0,插入链表后会修改,如果是尾节点,那么不会修改,这样保证了链表尾节点的next指针的值永远是0

    • 迭代器在移动的过程中, 移动链表尾部时,迭代器中的值就是0,也就是iterator(0)

    • 所以使用end()方法返回iterator(0),刚好等于迭代器移动到链表尾部的值

猜你喜欢

转载自www.cnblogs.com/chusiyong/p/11574115.html