zoukankan      html  css  js  c++  java
  • 【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


  • 相关阅读:
    matlab--“下标索引必须为正整数类型或逻辑类型”
    将中缀表达式转化为后缀表达式
    MATLAB那些常见的命令
    关于实现线程同步的几种方式
    关于http协议
    小白学习之activiti工作流入门
    小白- jquery 学习笔记
    小白-Javascript学习笔记
    小白-css笔记
    小白- html笔记
  • 原文地址:https://www.cnblogs.com/linuxAndMcu/p/14622491.html
Copyright © 2011-2022 走看看