STL学习笔记--4、序列式容器之vector

常见的数据结构:array数组,list链表,tree树,stack栈,queue队列,hash table散列表,set集合,map映射……

根据数据在容器中的排列分为:序列式sequence和关联式associative。

序列式容器之vector


1、vector VS array:

  1. array是静态空间,一旦配置则无法改变;
  2. vector是动态空间,随着元素的加入,内部机制会自动扩充新的空间来容纳新的元素。

    实现技术:对大小的控制和重新配置时的数据移动效率。

    扩充空间:配置新空间、数据移动、释放旧空间。


2、定义摘要:

在SGI中,#include<stl_vector.h>

在STl标准中只需要#include<vector>

template <class T, class Alloc = alloc>
class vector {
public:
    //嵌套型别定义
    typedef T value_type;
    typedef value_type* pointer;
    //迭代器类型为随机存取类型
    //RandomAccessIterator
    typedef value_type* iterator;
    typedef value_type& reference;
    typedef size_t size_type;

protect:
    //方便以元素为单位配置大小
    typedef simple_alloc<value_type, Alloc> data_allocator;

    //表示目前使用的空间头
    iterator start;
    //表示目前使用的空间尾
    iterator finish;
    //表示目前可用的空间尾
    iterator end_of_storage;

    //插入元素。有备用空间直接插入,无备用空间则进行空间分配
    void insert_aux(iterator position, const T& x);

    //调用全局deallocate来完成
    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; }
    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() const { return begin() == end(); }
    reference operator[](size_type n) { 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() {
        destroy(start, finish);
        deallocate();
    }

    //第一个元素引用
    reference front() { return *begin(); }
    //最后一个元素引用
    reference back() { return *(end() - 1); }

    //push_back,调用了insert_aux
    void push_back(const T& x) {
    if (finish != end_of_storage) {//还有备用空间
      construct(finish, x);
      ++finish;
    }
    else//以无备用空间
      insert_aux(end(), x);
    }

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

    //erase版本一:接受一个迭代器
    iterator erase(iterator position) {
        if (position + 1 != end())
          copy(position + 1, finish, position);
        --finish;
        destroy(finish);
        return position;
    }
    //erase版本二:接受二个迭代器。清除迭代器范围的元素
    iterator erase(iterator first, iterator last) {
        iterator i = copy(last, finish, first);
        destroy(i, finish);
        finish = finish - (last - first);
        return first;
    }

    //resize():为容器重新定义长度。
    //若new_size小于size则,丢弃多余部分
    //若new_size大于size则,空出的部分进行T类型的值初始化
    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());}

    //clear()清空容器
    void clear() { erase(begin(), end()); }

protected:
    //配置容器并填满内容,填n个x
    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));
  }

3、vector迭代器

vector维护的是连续线性空间。支持随机存取,迭代器类型为RandomAccessIterator。


4、vector数据结构

    //表示目前使用的空间头
    iterator start;
    //表示目前使用的空间尾
    iterator finish;
    //表示目前可用的空间尾
    iterator end_of_storage;

vector实际配置的大小总是大于等于客户端需求的大小,以备将来的扩充。

增加新元素时,如果超出当时容量,则容量进行两倍扩充;若两倍容量不足时,则扩充足够大的容量。

容量的扩充需要进行:重新配置、元素移动、释放旧空间


5、构造和内存管理:

vector缺省使用alloc作为空间配置器;

uninitialized_fill_n()根据第一个参数的型别特性type traits决定算法使用(__true_type)fill_n()或是反复调用(__false_type)construct()来完成。

    //push_back,调用了insert_aux
    void push_back(const T& x) {
    if (finish != end_of_storage) {//还有备用空间
      construct(finish, x);
      ++finish;
    }
    else//以无备用空间
      insert_aux(end(), x);
    }

算法实现:insert_aux(end(), x);

template <class T, class Alloc>
void vector<T, Alloc>::insert_aux(iterator position, const T& x) {
    if (finish != end_of_storage) {//还有备用空间
        //在备用空间起始处构造元素,以finish - 1位置处的值作为初值
        construct(finish, *(finish - 1));
        ++finish;
        T x_copy = x;
        //将[position,finish-2)处的值拷贝到[(finish - 1)-((finish - 2)-(position)),finish - 1)
        copy_backward(position, finish - 2, finish - 1);
        *position = x_copy;
    }
  else {//无备用空间
    const size_type old_size = size();
    //确定新构造的空间的长度
    //若旧空间为0,则构造1个
    //若旧空间非零,则构造旧空间的两倍
    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 {
        //旧空间的元素拷贝到新空间
        new_finish = uninitialized_copy(start, position, new_start);
        //新元素指定为x
        construct(new_finish, x);
        //调整使用空间大小
        ++new_finish;
        //新元素装入
        new_finish = uninitialized_copy(position, finish, new_finish);
    }

#ifdef  __STL_USE_EXCEPTIONS
    //若新空间分配失败,则释放所有新分配的空间
    catch(...) {
      destroy(new_start, new_finish);
      data_allocator::deallocate(new_start, len);
      throw;
    }
#endif /* __STL_USE_EXCEPTIONS */

    //释放旧空间
    destroy(begin(), end());
    deallocate();

    //调整迭代器,指向新空间
    start = new_start;
    finish = new_finish;
    end_of_storage = new_start + len;
  }
}

6、元素操作:erase()

两个参数版本,释放局部区间的清除操作示意图:


7、元素操作:insert()

insert(iterator position, size_type n, const T& x)

1、若备用空间大于等于新增元素个数
(end_of_storage-finish) >= n

    计算插入点之后现有的元素个数
    elems_after=finish-position;

    情况一:1)、若**插入点之后的现有元素个数**大于**新增元素个数**
    elems_after>n
    {
    uninitialized_copy(finish - n, finish, finish);
    finish += n;
    copy_backward(position, old_finish - n, old_finish);
    fill(position, position + n, x_copy);
    }

    情况二:2)、若**插入点之后的现有元素个数**小于等于**新增元素个数**
    elems_after<=n
    {
    uninitialized_fill_n(finish, n - elems_after, x_copy);
    finish += n - elems_after;
    uninitialized_copy(position, old_finish, finish);
    finish += elems_after;
    fill(position, old_finish, x_copy);
    }
2、若备用空间小于新增元素个数
(end_of_storage-finish) < n

    1)决定新的长度。旧长度的两倍或旧长度+新增元素个数。
    len=old_size+max(old_size,n)

    2)配置内存空间
    iterator new_start =data_allocator::allocate(len);
    iterator new_finish = new_start;

    情况三:3)数据元素的移动
    {
    //旧空间的插入点之前的元素copy到新空间
    new_finish = uninitialized_copy(start, position, new_start);
    //新增的元素初值为x,填入到新空间
    new_finish = uninitialized_fill_n(new_finish, n, x);
    //旧空间的插入点之后的元素copy到新空间
    new_finish = uninitialized_copy(position, finish, new_finish);
    }

3、如果分配新空间出现异常,则销毁新分配的内存,抛出异常
# ifdef  __STL_USE_EXCEPTIONS
      catch(...) {
        destroy(new_start, new_finish);
        data_allocator::deallocate(new_start, len);
        throw;
      }
#endif /* __STL_USE_EXCEPTIONS */

4、清除并释放旧空间内存
      destroy(start, finish);
      deallocate();

5、调整迭代器,指向新空间
      start = new_start;
      finish = new_finish;
      end_of_storage = new_start + len;

情况一:备用空间充足,且elems_after>n

n=2; elems_after=2; 备用空间end_of_storage-finish=2;

情况二:备用空间充足,且elems_after<=n

n=4; elems_after=2; 备用空间end_of_storage-finish=3;

情况三:备用空间不足容纳新增元素

n=3; 备用空间end_of_storage-finish=2;

时间: 2024-10-18 13:45:29

STL学习笔记--4、序列式容器之vector的相关文章

STL 源码剖析读书笔记三:序列式容器之 vector、list

1. STL 中的容器 容器,置物之所也.STL 容器即是将运用最广的一些数据结构实现出来.如下图所示: 上图以内缩方式来表达基层和衍生层的关系.所谓衍生,并非派生关系,而是内含关系.例如 heap 内含一个 vector,priority-queue 内含一个 heap.stack.queue都内含一个 deque,set/map/multimap/multiset 都内含一个 RB-tree,hash_x 都内含一个 hashtable. 2. 序列式容器之 vector 所谓序列式容器,其

C++ 序列式容器之vector

什么是容器 容器,顾名思义,是用来容放东西的场所.C++容器容放某种数据结构,以利于对数据的搜寻或排序或其他特殊目的.众所周知,常用的数据结构不外乎:数组array,  链表list,  树tree,  栈stack,  队列queue,  散列表hash table,  集合set.映射表map 等等.容器便是容纳这些数据结构的.这些数据结构分为序列式与关联式两种,故容器也分为序列式容器和关联式容器.   (图来自<STL源码剖析>) vector 概述 1.  vector是STL提供的一

STL学习笔记(序列式容器)

Vector Vector是一个动态数组. 1.Vector的操作函数 构造.拷贝和析构 vector<Elem> c //产生一个空vector ,其中没有任何元素 vector<Elem> c1(c2) //产生另一个同型vector的副本,(所有元素都被拷贝) vector<Elem> c(n) //利用元素的default构造函数生成一个大小为n的vector vector<Elem> c(n,elem) //产生一个大小为n的vector,每个元素

STL源码笔记(12)—序列式容器之deque(二)

STL源码笔记(12)-序列式容器之deque(二) 再谈deque数据结构 我们知道deque是通过map管理很多个互相独立连续空间,由于对deque_iterator的特殊设计,使得在使用的时候就好像连续一样.有了deque_iterator的基础(例如重载的操作符等),对于我们实现容器的一些方法就十分方便了.与vector一样,deque也维护一个start,和finish两个迭代器,start指向容器中的一个元素,finish指向最后一个元素的后一个位置(前闭后开),从微观上讲,star

STL 源码剖析读书笔记四:序列式容器之 deque、stack、queue

1. 序列式容器 deque 1.1 deque 概述 vector是单向开口的连续线性空间,用户只能在vector尾部进行插入删除操作,而 deque 是一种双向开口的连续线性空间,允许我们在头尾两端操作. deque 和 vector 的最大差异在于: deque 允许常数时间对头端元素进行插入和移除操作 deque 没有所谓容量(capacity)概念,因为它是动态地以分段连续的空间组合而成,随时可以增加一段新的空间并链接起来 deque提供的迭代器也是 RandomAccess Iter

stl源码剖析-序列式容器 之 vector

vector的数据安排以及操作方式,与array(c++自身提供的序列式容器)非常相似.两者唯一的差别在于空间的运用的灵活性.array是静态空间,一旦配置了将不能随意更改其大小,若要更改需要重新配置一块新的空间,如何将元素从旧址中一一搬迁,再释放原来的系统.而vector是动态空间,随着元素的加入,它的内部机制会自行扩充空间已容纳新元素.(实际vector每次动态分配的空间大小是有限的,超过了这个临界值同样是要像array一样进行元素搬迁,下面将会进行详细介绍)vector 是最常用的 C++

STL学习笔记--4、序列式容器之list

1.概述 list:地址不连续的空间.每次插入或删除一个元素,就配置或释放一个元素空间.对于任意位置的元素插入或删除,list永远是常数时间. 2.list节点 list本身和list节点是不同的: template <class T> struct __list_node { typedef void* void_pointer; //void_pointer实际为__list_node<T>* void_pointer next; void_pointer prev; T da

STL序列式容器之list

一,list容器基本概念 1.list容器基本知识 list容器的底部数据结构为双向链表,可以高效的进行插入和删除元素. list因为底层数据结构是双向链表,因此不支持下标操作和.at()函数的操作.要获取元素,必须从头到尾遍历. 使用list容器必须引入头文件# include<list>. 二,list容器构造函数 1.无参构造函数 // 无参构造函数 list<string> l1; 2.有参构造函数 // 有参构造函数,10个字符'A'来初始化容器 list<char

STL学习笔记--&gt;初识STL

“这里要用char类型”; “这里要用int类型”; “其实实现这个方法只需要把另一个方法的返回值的类型和传入参数的类型改成float类型就实现了”; “其实这个算法只需要把以前写的那个稍微改动一下就行了”; ……………… 学过面向对象语言的都知道GP这个概念,就是泛型程序设计,说的再明白点就是编写不依赖于具体数据类型的程序,C++作为一门面向对象语言,当然也有泛型这个概念,这就不得不提STL(Standard Template Library,标准模板库),是被融入C++标准程序库里面的一个高