SGI STL 源码 vector 分析

前言

vector 是最常用的 C++ 容器,其动态扩容的特性是普通数组无法具备的,大大增加了编程的灵活性。虽然能基本理解其基本原理,但也无法深层次理解,直到研读了 vector 的源码,才能比较自信的说自己真正理解了 vector 的基本原理,正应了侯捷说的那句话:源码面前,了无密码。我会分两篇文章分别分析泛化 vector 和针对 bool 类型的特化 vector(即是 bit_vector,位vector)。本文将分析泛化的 vector 的源码。

vector概述

vector 是动态扩容的连续数组。普通数组是静态空间,一旦配置就无法改变,而 vector 是动态空间,其内部机制会自动扩充空间以容纳更多的元素。其动态扩容的具体过程是:当容器没有备用空间时,会开辟一块是旧空间两倍大小的新空间,将数据从旧空间复制到新空间并释放原空间。因此,vector 对内存的合理利用和运用的灵活性有很大帮助,我没再也不用考虑数组的容量不足的问题。

vector 源码分析

本文分析的 vector 源码是侯捷老师《STL源码剖析》用的 SGI-STL-v2.91 的版本。其泛化版本的 vector 具体实现在 stl_vector.h 文件中,部分源码如下:

// alloc 是 SGI STL 的空间配置器
template<class T, class Alloc = alloc>
class vector {
public:
    typedef T value_type;
    typedef value_type* pointer;
    typedef const value_type* const_pointer;
    typedef value_type* iterator;
    typedef const value_type* const_iterator;
    typedef value_type& reference;
    typedef const value_type& const_reference;
    typedef size_t size_type;
    typedef ptrdiff_t difference_type;

#ifdef __STL_CLASS_PARTIAL_SPECIALIZATION  //这里是反转迭代器,可先忽略
  typedef reverse_iterator<const_iterator> const_reverse_iterator;
  typedef reverse_iterator<iterator> reverse_iterator;
#endif

protected:
    // simple_alloc 是 SGI STL 的空间配置器,是源码所有容器都使用这个接口
    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);
    }
    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; }
    const_iterator begin() const { return start; }
    iterator end() { return finish; }
    const_iterator end() const { return finish; }
    reverse_iterator rbegin() { return reverse_iterator(end()); }
    const_reverse_iterator rbegin() const {
        return const_reverse_iterator(end());
    }
    reverse_iterator rend() { return reverse_iterator(begin()); }
    const_reverse_iterator rend() const {
        return const_reverse_iterator(begin());
    }
    size_type size() const {
        return size_type(end() - begin());
    }
    size_type max_size() const {
        return size_type(-1) / sizeof(T);
    }
    size_type capacity() const {
        return size_type(end_of_storage - begin());
    }
    bool empty() const { return begin() == end(); }

    reference operator[](size_type n) {
        return *(begin() + n);
    }
    const_reference operator[](size_type n) const {
        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(const vector<T, Alloc>& x) {
        start = allocate_and_copy(x.end() - x.begin(), x.begin(), x.end());
        finish = start + (x.end() - x.begin());
        end_of_storage = finish;
    }
    template <class InputIterator>
    vector(InputIterator first, InputIterator last) :
        start(0), finish(0), end_of_storage(0)
    {
        range_initialize(first, last, iterator_category(first));
    }

    ~vector() {
        destroy(start, finish);  //全局变量
        deallocate();
    }

    vector<T, Alloc>& operator=(const vector<T, Alloc>& x);

    void reverve(size_type n) {
        if (capacity() < n) {
            const size_type old_size = size();
            iterator tmp = allocate_and_copy(n, start, finish);
            destroy(start, finish);
            deallocate();
            start = tmp;
            finish = start + old_size;
            end_of_storage = start + n;
        }
    }

    reference front() { return *begin(); }
    const_reference front() const { return *begin(); }
    reference back() { return *(end() - 1); }
    const_reference back() const { return *(end() - 1); }

    void push_back(const T& x) {
        if (finish != end_of_storage) {  //还有备用空间
            construct(finish, x);  //全局函数
            ++finish;
        } else  //无备用空间
            insert_aux(end(), x);  //成员函数,后续会分析
    }

    void swap(vector<T, Alloc>& x) {
        __STD::swap(start, x.start);
        __STD::swap(finish, x.finish);
        __STD::swap(end_of_storage, x.end_of_storage);
    }
    
    //和push_back差别不大,只是插入位置不一样,复杂一点
    iterator insert(iterator position, const T& x) {  
        size_type n = position - begin();
        if (finish != end_of_storage && position == end()) {
            construct(finish, x);
        } else 
            insert_aux(position, x);

        return begin() + n;
    }
    
    iterator insert(iterator position) {
        insert(position, T());
    }

    #ifdef __STL_MEMBER_TEMPLATES
    template <class InputIterator>
    void insert(iterator position, InputIterator first, InputIterator last) 
    {
        range_insert(position, first, last, iterator_category(first));
    }
    #endif

    void insert(iterator pos, size_type n, const T& x);

    void insert(iterator pos, int n, const T& x) {
        insert(pos, (size_type) n, x);
    }
    
    void insert(iterator pos, long n, const T& x) {
        insert(pos, (size_type) n, x);
    }

    void pop_back() {
        --finish;
        destroy(finish);    //finish->~T 这里仅仅是调用指针finish所指对象的析构函数,不能释放内存
    }
  
    iterator erase(iterator position) {
        //如果移除的不是最后一个元素
        if (position + 1 != end())
            copy(position + 1, finish, position);  //全局函数

        --finish;
        destroy(finish);
        return position;
    }

    //移除半开半闭区间[first, last)之间的所有元素,last指向的元素不被移除
    iterator erase(iterator first, iterator last) {
        iterator i = copy(last, finish, first);  
        //如果区间内元素的析构函数是trivial的,则什么也不做
        //如果区间内元素的析构函数是non-trivial的,则依序调用其析构函数
        destroy(i, finish);    
        finish = finish - (last - first);    //重新调整finish
        return first;
    }
    
    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);
        __STL_TRY 
        {
            uninitialized_fill_n(result, n, x);
            return result;
        }
        __STL_UNWIND(data_allocator::deallocate(result, n));
    }
      template <class ForwardIterator>
      iterator allocate_and_copy(size_type n,
                                 ForwardIterator first, ForwardIterator last) {
          iterator result = data_allocator::allocate(n);
          __STL_TRY {
              uninitialized_copy(first, last, result);
              return result;
          }
          __STL_UNWIND(data_allocator::deallocate(result, n));
      }
/***********************后续还有********************************/

vector 迭代器

vector 维护的是一个连续空间所以无论其元素的型别为何,普通指针就可以作为 vector 的迭代器而满足所有必要条件,因为 vector 迭代器所需要的操作行为,如 operator*,operator++,operator--,operator+,operator-,operator+=,operator-=,普通指针就具备,从上面的源码就可以看出,vector 用的迭代器就是普通指针。但除 bool 类型,虽然普通指针一样适用,为提高空间利用率,源码为其设计的特化版本有设计专门的迭代器,下一篇文章会进一步探讨。

insert_aux 分析
insert_aux 是 vector 实现动态扩容的关键,具体源码如下:
template <class T, class Alloc>
void vector<T, Alloc>::insert_aux(iterator position, const T& x) {
    if (finish != end_of_storage) {  //还有备用空间
        //在备用空间的起始处构造一个元素,并以 vector 最后一个元素值为其初值
        construct(finish, *(finish - 1));
        ++finish;
        T x_copy = x;
        //全局函数,从后面finish-1往前复制[position,finish-2]的值
        copy_backward(position, finish - 2, finish - 1); 
        *position = x_copy;
    } else {
        const size_type old_size = size();
        const size_type len = old_size != 0 ? 2 * old_size : 1;  //为当前空间两倍大小
        iterator new_start = data_allocator::allocate(len);
        iterator new_finish = new_start;
        __STL_TRY
        {
            //复制[start,position)复制到new_start,new_finish为new_start+(position-start)
            new_finish = uninitialized_copy(start, position, new_start);
            construct(new_finish, x); 
            ++new_finish; //调整水位
            //复制[position,finish)复制到new_finish
            new_finish = uninitialized_copy(position, finish, new_finish);
        }
        catch(...) {
            destroy(new_start, new_finish); 
            data_allocator::deallocate(new_start, len);
            throw;
        }
        destroy(begin(), end()); //调用旧空间元素的析构函数
        deallocate(); //释放旧空间
        start = new_start;
        finish = new_finish;
        end_of_storage = new_start + len;
    }
}

 从上面的源码可以看出,所谓动态扩容,并不是在原空间之后接续新空间,因为无法保证原空间之后尚有可配置的空间。而是以原大小的两倍另外配置一块较大空间,然后将原内容拷贝过来(这里注意技巧,分两步,先拷贝[start,position),在新空间需空出position的位置,再拷贝[position,finish)到新空间),然后才在position位置构造新元素,并释放原空间。因此对 vector 的任何操作需要注意,当引起空间重新配置时,指向原 vector 的所有迭代器就都失效。

猜你喜欢

转载自www.cnblogs.com/evenleee/p/11669423.html
今日推荐