1. 程式人生 > >STL學習筆記--4、序列式容器之vector

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;
這裡寫圖片描述