zoukankan      html  css  js  c++  java
  • STL—vector

    前面介绍了STL对象的构造与析构以及内存的配置与释放,那具体的容器是怎么应用STL的空间配置器的呢?这篇先介绍STL的容器vector。

    vector的数据成员

    vector只有4个数据成员:3个迭代器、1个内存配置器。

    STL会为每个容器都设置一个内存配置器的成员,这里的内存配置器就是前面介绍的STL空间配置器,使用了统一对外接口的类simple_alloc,即STL会为每个容器都定义一个simple_alloc类的类型成员,通过该类型成员来为容器分配内存。

    vector的迭代器就是原始指针,只不过用了typedef将迭代器的类型变为了iterator,其实它就是T* 。vector的3个迭代器分别指向当前内存的起始地址(start)、最后一个数据的尾后地址(finish)、整个内存的最后地址(end_of_storage)。源码如下:

    class vector
    {
    public:
            typedef T                       value_type;
            typedef value_type*             pointer;
            typedef const value_type*       const_pointer;
            typedef value_type*             iterator;//vector迭代器就是一个原生指针
            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;
            typedef MiniSTL::reverse_iterator<iterator>             reverse_iterator;
            typedef MiniSTL::reverse_iterator<const_iterator>       const_reverse_iterator;
            typedef alloc allocator_type;
    
            allocator_type get_allocator() const { return allocator_type(); }
    
    
    private:
            typedef simple_alloc<T, allocator_type> data_allocator;
            iterator        start;
            iterator        finish;
            iterator        end_of_storage;

    vector对象的构造

    vector有多种构造函数,但做的事情都一样,即先调用内存配置器去分配一块内存,然后对这块内存初始化,最后设置3个迭代器成员,让它们指向正确的位置。

    以我们平常使用最多的vector构造方法,如:vector<int> vec(10); 为例,其对应的构造函数如下,下面还将该构造过程涉及到的函数一并列出:

    构造函数vector(size_type n)调用fill_initializer函数,该函数会调用allocate_and_fill函数先去分配一块内存,然后进行初始化,由于这种构造方式未提供初始值,则按T类型的默认初始化进行初始化,然后剩下工作就是设置好start、finish、end_of_storage迭代器,工作便完成。

            void fill_initializer(size_type n, const T& value)
            {
                    start = allocate_and_fill(n, value);
                    finish = start + n;
                    end_of_storage = finish;
            }
    
            iterator allocate_and_fill(size_type n, const T& value)
            {
                    iterator result = data_allocator::allocate(n);
                    uninitialized_fill_n(result, n, value);
                    return result;
            }
    
    public:
            vector() : start(0), finish(0), end_of_storage(0) {}
            explicit vector(size_type n) { fill_initializer(n, T()); }

    vector空间的动态增长

    当我们向vector进行push_back时,若原内存空间未满,那很好,直接在后面添加一个元素即可。若原内存空间已满,则不能直接在其后面添加了,因为谁也不知道原空间后面的内存到底是魔鬼还是天使。

    所以,若原空间内存已满,继续往vector添加元素时,会先调用内存配置数据成员,分配一块新的内存,为了减少内存分配的次数,所以既然要分配了,那干脆就多分点,所以这块新内存的大小为原空间内存的2倍。

    接着,将原内存上的数据拷贝到新内存中--->析构原内存空间中的对象--->释放原内存空间--->重新设置迭代器。

    vector添加元素时,会导致内存空间的重新分配,所以会导致之前的迭代器都失效。

    vector要是经常这样动态增长会导致程序效率下降,所以可以调用vector的reserve函数预先分配一大块指定大小的内存,以减少内存重分配次数。

    对照下面源码分析。当finish和end_of_storage相等,则知道已经没有剩余空间了,push_back会调用insert_aux函数完成剩下全部工作。insert_aux函数调用allocate函数分配原空间2倍大小的新内存空间,调用uninitialized_copy函数将原内存中数据拷贝到新内存,接着调用destroy析构原空间中对象,调用deallocate()释放原内存空间,并重新设置start、finish、end_of_storage迭代器。如下:

    push_back : 

            void push_back(const T& val)
            {
                    if (finish != end_of_storage)
                    {
                            construct(finish, val);
                            ++finish;
                    }
                    else
                            insert_aux(end(), val);
            }

    insert_aux : 

    template<typename T>
    void
    vector<T>::insert_aux(iterator position, const T& x)
    {
            if (finish != end_of_storage)  //还有备用空间
            {
                    construct(finish, *(finish - 1));
                    ++finish;
                    T x_copy = x;
                    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;  //2倍原空间大小
                    iterator new_start = data_allocator::allocate(len);
                    iterator new_finish = new_start;
    
                    try {
                            new_finish = uninitialized_copy(start, position, new_start);
                            construct(new_finish, x);
                            ++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, end_of_storage - start); // 释放原内存空间
                    start = new_start;
                    finish = new_finish;
                    end_of_storage = new_start + len;
            }
    }
    View Code

    有关vector空间的动态增长的详细介绍可参考我的另一篇文章:http://www.cnblogs.com/zxiner/p/7197327.html

     

    (全文完)

    附:
    一款简易版STL的实现,项目地址:https://github.com/zinx2016/MiniSTL/tree/master/MiniSTL
  • 相关阅读:
    动态生成 Excel 文件供浏览器下载的注意事项
    JavaEE 中无用技术之 JNDI
    CSDN 泄露用户密码给我们什么启示
    刚发布新的 web 单点登录系统,欢迎下载试用,欢迎提建议
    jQuery jqgrid 对含特殊字符 json 数据的 Java 处理方法
    一个 SQL 同时验证帐号是否存在、密码是否正确
    PostgreSQL 数据库在 Windows Server 2008 上安装注意事项
    快速点评 Spring Struts Hibernate
    Apache NIO 框架 Mina 使用中出现 too many open files 问题的解决办法
    解决 jQuery 版本升级过程中出现 toLowerCase 错误 更改 doctype
  • 原文地址:https://www.cnblogs.com/zxiner/p/7197332.html
Copyright © 2011-2022 走看看