《STL源碼剖析》筆記:vector

vector 的數據安排以及操作方式,與array常常相似,兩者的唯一差別在于空間運用的靈活性:array是靜態空間,一旦配置了不能改變。vector動態空間,可以根據需要,自行擴充空間以容納新元素。

vector的實現技術關鍵在于其對大小控制以及重新配置時的數據移動效率。因為重新申請內存,拷貝原數據,釋放原vector是要花很長時間。所以在滿載時,申請新空間的大小要在空間和時間上取出平衡。

vector結構很簡單:

vector.png
template <typename T>
class Vector 
{
    ......
    typedef allocator<T> dataAlloc;
    private:
        T* _start;
        T* _finish;
        T* _end_of_storage;
}

迭代器

typedef T* iterator;
typedef const T *const_iterator

原生指針就可以充當迭代器。

添加元素

push_back(const T &value)
  • 有空間則直接用 construct 構造元素。
  • 沒有空間則先重新分配新的內存,然后再把以前的元素拷貝之后,構造元素。也是因此,迭代器會失效
if (size() == capacity())
    reallocateAndCopy();
dataAlloc::construct(_finish++, value);
insert(iterator position, const size_type n, const T &value)
  • 備用空間足夠
    2 種情況:
    • a: 插入點后現有元素個數 >= 新增元素個數
    • b: 插入點后現有元素個數 <= 新增元素個數
  • 備用空間不足
    • 第一步:將原vector [ _start, position )拷貝至新的 vector
    • 第二步:構造n個value
    • 第三步:將 [ position, _finish )元素也拷貝至新的 vector
    • 第四步:析構并釋放原vector
/* 備用空間足夠 */
if (_end_of_storage - _finish >= n) {
    size_type elems_after = _finish - position;
    iterator old_finish = _finish;
    if (elems_after > n) {
        /* 插入點后現有元素個數 >= x新增元素個數 */
        uninitialized_copy(position, old_finish - n, old_finish);
        _finish += n;
    } else {
        /* 插入點后現有元素個數 <= x新增元素個數 */
        uninitialied_fill_n(_finish, n - elems_after, value);
        _finish += n - elems_after;
        uninitialized_copy(position, old_finish, _finish);
        _finish += elems_after;
        fill(position, old_finish, value);
    }
}
/* 備用空間不夠 */
else {
    size_type old_size = size();
    size_type len = old_size + max(old_size, n); // 新長度為舊長度的2倍,或者舊長度+新元素個數

    iterator new_start = dataAlloc::allocate(len); 
    iterator new_finish = new_start;

    try{
        /* 將原 vector[_start, position) 拷貝至新的vec */
        new_finish = uninitialized_copy(_start, position, new_start);
        /* 構造 n 個value */
        new_finish = uninitialied_fill_n(new_finish, n, value);
        /* 將 [position, _finish) 元素也拷貝過來 */
        new_finish = uninitialized_copy(position, _finish, new_finish);
    } catch(...) {
        destory(new_start, new_finish);
        dataAlloc::deallocate(new_start, len);
        throw;
    }
    /* 析構并釋放原vector */
    destroyAndFree();

    _start = new_start;
    _finish = new_finish;
    _end_of_storage = _start + len;
}
備用空間足夠,插入點后現有元素個數 <= 新增元素個數.png

修改容量

resize
  • n > capacity()需要重新分配內存空間,復制原size()元素,釋放原資源,增加(n - size())個元素
  • size() < n <= capacity 不需要重新分配內存空間,增加(n - capacaity())個元素
  • n == size() 什么都不做
  • n < size() 需要析構(size() - n)個元素
void resize(size_type n, const T &value) {
    if (n > capacity()) {
        size_type addElementLength = n - size();
        /* 重新分配內存 */
        T *newStart = dataAlloc::allocate(n);
        T *newFinish = uninitialized_copy(begin(), end(), newStart);
        /* 拷貝元素 */
        newFinish = uninitialied_fill_n(newFinish, addElementLength, value);
        /* 銷毀原內存 */
        destroyAndFree();
        /* 更新相關指針 */
        _start = newStart;
        _finish = newFinish;
        _end_of_storage = _start + n;
    } else if (n <= capacity() && n > size()) {
        size_type addElementLength = n - size();
        _finish = uninitialied_fill_n(end(), addElementLength, value);
    } else if (n < size()) {
        dataAlloc::destroy(begin() + n, end());
        _finish = _start + n;
    }
}
reserve
  • n > capacity() 重新分配內存空間,復制原size()元素,釋放原資源。
void reserve(size_type n) {
    if (n > capacity()) {
        T *new_start = dataAlloc::allocate(n);
        T *new_finish = uninitialized_copy(begin(), end(), new_start);

        destroyAndFree();

        _start = new_start;
        _finish = new_finish;
        _end_of_storage = _start + n;
     }
}

刪除元素

void pop_back()
void Vector<T>::pop_back() {
    dataAlloc::destroy(--_finish);
}
iterator erase(iterator first, iterator last)
size_type remove_element_counts = last - first;     // 要刪除的個數

if (remove_element_counts > 0) {
    auto it = first;
    for (; last != _finish; last++, it++)
        *it = *last;
    dataAlloc::destroy(it, _finish);
    _finish = _finish - remove_element_counts;
}

return first;
iterator erase(iterator first, iterator last).png
iterator erase(iterator position)
return erase(position, position + 1);
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 標簽(空格分隔): STL 運用STL,可以充分利用該庫的設計,讓我為簡單而直接的問題設計出簡單而直接的解決方案,...
    認真學計算機閱讀 1,506評論 0 10
  • 前言 把《C++ Primer》[https://book.douban.com/subject/25708312...
    尤汐Yogy閱讀 9,536評論 1 51
  • vector和string 所有的STL容器都很有用,但是相比于其他容器,vector和string更常用。本章從...
    lintong閱讀 1,298評論 0 3
  • 哈哈紅紅火火恍恍惚惚
    習慣Mok閱讀 154評論 0 0
  • 1、(芳蕊玫瑰--青春內雕)這個在福建已經做的非常火爆了、目前在市場上已經幫助了無數的女性。青春內雕項目能夠讓我們...
    游帥來也閱讀 2,137評論 0 0