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


  • 相关阅读:
    Atitit 集团与个人的完整入口列表 attilax的完整入口 1. 集团与个人的完整入口列表 1 2. 流量入口概念 2 3. 流量入口的历史与发展 2 1.集团与个人的完整入口列表
    atitit 每季度日程表 每季度流程 v3 qaf.docx Ver history V2 add diary cyar data 3 cate V3 fix detail 3cate ,
    Atitit react 详细使用总结 绑定列表显示 attilax总结 1. 前言 1 1.1. 资料数量在百度内的数量对比 1 1.2. 版本16 v15.6.1 1 1.3. 引入js 2
    Atitit r2017 r3 doc list on home ntpc.docx
    Atitit r2017 ra doc list on home ntpc.docx
    Atiitt attilax掌握的前后技术放在简历里面.docx
    Atitit q2016 qa doc list on home ntpc.docx
    Atitit r7 doc list on home ntpc.docx 驱动器 D 中的卷是 p2soft 卷的序列号是 9AD0D3C8 D:\ati\r2017 v3 r01\
    Atitit 可移植性之道attilax著
    Atitit q2016 q5 doc list on home ntpc.docx
  • 原文地址:https://www.cnblogs.com/linuxAndMcu/p/14622491.html
Copyright © 2011-2022 走看看