【STL源码剖析】vector

一、vector 概述

vector 的数据安排以及操作方式,与 array 非常相似。两者的唯一差别在于空间的运用的灵活性。array 是静态空间,一旦配置了就不能改变。 vector 是动态空间,随着元素的加入,它的内部机制会自行扩充空间以容纳新元素。因此, vector 的运用对于内存的合理利用与运用的灵活性有很大的帮助,我们再也不必因为害怕空间不足而开始就要求一个大块头 array 了,我们可以安心使用 vector,吃多少用多少。

vector 的实现技术,关键在于其对大小的控制以及重新配置时的数据移动效率。一旦 vector 旧有空间满载,如果客户端每新增一个元素, vector 内部只是扩充一个元素的空间,实为不智,因为所谓扩充空间(不论多大),一如稍早所说是 “配置新空间/数据移动/释还旧空间” 的大工程,时间成本很高,应该加人某种未雨绸缪的考虑。稍后我们便可看到 SGI vector 的空间配置策略。

二、vector 定义摘要

以下是 vector 定义的源代码摘录。虽然 STL 规定,欲使用 vector 者必须先包括<vector>,但 SGI STL 将 vector 实现于更底层的<stl_vector h>

以下是 vector 定义的部分源代码摘录:

// alloc是SGI STL的空间配置器,见第二章
template <class T, class Alloc = alloc>
class vector
{
public:
    // vector的嵌套类型定义
    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:
    // 这个提供STL标准的allocator接口
    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()
    {
        // 由于使用的是data_allocator进行内存空间的分配,
        // 所以需要同样使用data_allocator::deallocate()进行释放
        // 如果直接释放, 对于data_allocator内部使用内存池的版本
        // 就会发生错误
        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; }
    iterator end() { return finish; }

    // 返回当前对象个数
    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); }

    // 默认构造出的vector不分配内存空间
    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;
    }

    ~vector()
    {
        // 析构对象(全局函数,见2.2.3节)
        destroy(start, finish);
        // 释放内存(这是 vector 的一个 member function)
        deallocate();
    }

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

    // 提供访问函数
    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);
    }
    iterator insert(iterator position, const T& x)
    {
        size_type n = position - begin();
        if (finish != end_of_storage && position == end())
        {
            construct(finish, x);
            ++finish;
        }
        else
            insert_aux(position, x);
        return begin() + n;
    }
    
    iterator insert(iterator position) { return insert(position, T()); }

    void pop_back()
    {
        --finish;
        destroy(finish);
    }

    iterator erase(iterator position)
    {
        if (position + 1 != end())
            copy(position + 1, finish, position);
        --finish;
        destroy(finish);
        return position;
    }

    iterator erase(iterator first, iterator last)
    {
        iterator i = copy(last, finish, first);
        // 析构掉需要析构的元素
        destroy(i, finish);
        finish = finish - (last - first);
        return first;
    }

    // 调整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 维护的是一个连续线性空间,所以不论其元素型别为何,普通指针都可以作为 vector 的迭代器而满足所有必要条件,因为 vector 迭代器所需要的操作行为,如operator*operator-> operator++operator-- operator+operator operator+=operator-=,普通指针天生就具备。 vector 支持随机存取,而普通指针正有着这样的能力。

template <class T, class Alloc=alloc>
class vector {
public:
   typedef T value_type;
   typedef value_type* iterator; // vector的迭代器是普通指针
 ...
};

根据上述定义,如果客户端写出这样的代码:

vector<int>::iterator ivite;
vector<Shape>::iterator svite;

ivite 的类型其实就是int*,svite 的类型别其实就是Shape*

四、vector 的数据结构

template <class T,class Alloc=alloc>
class vector
{
...
protected:
     iterator start;          // 目前使用的空间头
     iterator finish;         // 目前使用的空间尾
     iterator end_of_storage; // 目前可用的空间尾
...
}

vector 采用的数据结构很简单,就是连续线性空间。以上定义中迭代器 start 和 finish 之间是已经被使用的空间范围,end_of_storage 是整块连续空间包括备用空间的尾部。end_of_storage 存在的原因是为了降低空间配置的成本,vector 实际分配空间大小的时候会比客端需求的大一些,以便于未来的可能的扩展。
运用这三个迭代器成员,就能提供 begin、end、size 等函数:

template <class T,class Alloc=alloc>
class vector
{
...
pubic:
     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() { return begin() = end(); }
     reference front { return *begin(); }
     reference operator[](size_type n) { return *(begin() + n); }
     reference front() { return *begin(); }
     reference back() { return *end() - 1; }     
...
}

为了降低空间配置时的速度成本, vector 实际配置的大小可能比客户端需求量更大一些,以备将来可能的扩充。这便是容量( capacity)的观念。换句话说,一个 vector 的容量永远大于或等于其大小。一旦容量等于大小,便是满载,下次再有新增元素,整个 vector 就得另觅居所,见下图:

这里写图片描述


参考:

《STL源码剖析》

STL源码剖析---vector


posted @ 2021-04-06 16:35  fengMisaka  阅读(711)  评论(0编辑  收藏  举报