STL源碼剖析(一)

SLT簡介

STL(Standard Template Library),即標準模板庫,是一個高效的C++程序庫。包含了諸多在計算機科學領域裏經常使用的基本數據結構和基本算法。爲廣大C++程序員們提供了一個可擴展的應用框架,高度體現了軟件的可複用性。其核心思想就是泛化編程(generic programming),在這種思想裏,大部分基本算法被抽象,被泛化,獨立於與之對應的數據結構,用於以相同或相近的方式處理各類不一樣情形。node

STL組件

STL中包含了6大組件程序員

  • 容器(Containers):包含各類基礎的數據結構,如vector, list, deque, set, map等。
  • 分配器(Allocators):負責空間配置與管理。
  • 算法(Algorithms):各類經常使用的算法,如sort, search, copy, erase等等。
  • 迭代器(Iterators):負責鏈接Containers與Algorithms。
  • 適配器(Adapters):能夠用來修飾Containers,Iterators和Functors接口的組件。
  • 函數式(Functors):相似於函數,能夠做爲Algorithms的一種策略。

六大組件的關係

Alt text
Containers 經過 Allocators 取得數據存儲空間,Algorithms 經過 Iterators 存取 Containers 內容,Functors 能夠協助 Algorithms 完成不一樣的策略變,Adapters 能夠修飾或套接Containers,Iterators和Functors。算法

容器

結構與分類

容器整體上分爲三大類:編程

  • Sequence Containers(序列容器): Arrary(大小固定,沒法自動擴充), Vector(只可向後擴充, 兩倍的擴展), Deque(可向前或向後擴充, 分段連續, stack和queue都是基於此 ), List(雙向鏈表), Forwaed-List(單向鏈表)
  • Associative Containers(關聯容器):Set/Multiset, Map/Multimap(基本都用紅黑樹來實現)
  • Unordered Containers(無序容器): Unordered Set/Multiset, Unordered Map/Multimap(基本都是 HashTable Separate Chaining 實現)

Alt text

Array

是一種固定大小的容器類型,在定義的時候就要聲明大小和類型。Array其實就是對C語言中數組的一種擴充升級,使其支持了迭代器的操做,便於STL算法的使用。array在使用和性能上都要強於內置數組,對於一些固定大小的使用場景,能夠用array來替代原先數組的工做。數組

TR1版本源碼以下:數據結構

template<typename _Tp, std::size_t _Nm>
  struct array
  {
    typedef _Tp value_type;
    typedef _Tp* pointer;
    typedef balue_type* iterator;

    value_type _M_instance[_Nm ? _Nm : 1];

    iterator begin()
    { return iterator(&_M_instance[0]);}

    iterator end()
    { return iteratoe(&_M_instance[_Nm]);}

    ...
  };

Vector

Vector 使用起來和一個數組十分類似,可是在空間用完時,能夠自動擴充本身的空間。通常而言空間的擴充,沒法在原地完成擴充。因此會在內存中新申請一片內存(一般都是以前空間大小的2倍大),而後經過拷貝將原有數據拷貝到新的地址空間。框架

Vector中存在三個指針來代表Vector:dom

  • T* start:指向第一個元素的地址
  • T* finish:指向目前最後一個地址以後的一個空間的地址
  • T* end_of_storage:指向當前Vector的最後一個空間地址

須要注意的是:在空間(兩倍)增加的過程當中涉及到了大量的拷貝構造和析構!函數

List

相較於vector的連續線性空間,List就顯得複雜許多,它的好處是每次插入或刪除一個元素,就配置或釋放一個元素空間。所以,list對於空間的運用有絕對的精準,一點也不浪費。並且,對於任何位置的元素插入或元素移除,List永遠是常數時間。性能

List不只是一個雙向鏈表,並且仍是一個環狀雙向鏈表。 另外,還有一個重要性質,插入操做和接合操做都不會形成原有的List迭代器失效,這在Vector是不成立的。由於Vector的插入操做可能形成空間的從新配置,致使原有的迭代器所有失效。甚至List的元素刪除操做(erase),也只有「指向被刪除元素」的那個迭代器失效,其餘迭代器不受任何影響。

Forward-List

Forward-List容器與List容器的主要設計區別是List保持內部惟一的一個連接到下一個元素,然後者則保持每一個元素的兩個連接:一個指向下一個元素和一個前一個。容許高效在兩個方向迭代,但每一個元素的消耗額外的存儲空間,並輕微較高的時間開銷插入和刪除元素的迭代。Forward-List對象,從而比List對象更有效率,雖然他們只能向前遍歷。

因此Forward-List的一個最大的缺點就是沒法直接訪問指定位置上元素,每次一的訪問都須要從頭開始訪問,這樣的操做須要線型的時間。

Deque

Alt text

能夠向兩端擴充,經過指針鏈接不一樣分段的空間,模擬出連續空間。

template <class T, class Alloc=alloc, size_t BufSiz=0>
class deque{
public:
    typedef T value_type;
    typedef __deque_iterator<T,T&,T*,BufSiz> iterator;
protected:
    typedef pointer* map_pointer;//T**
protected:
    iterator start;
    iterator finish;
    map_pointer map;
    size_type map_size;
public:
    iterator begin(){return start;}
    iterator end(){return finish;}
    size_type size(){return finish-start;}
...
};

template <class T, class Ref, class Ptr, size_t BufSiz>
  struct __deque_iterator{
    typedef random_access_iterator_tag iterator_category;
    typedef T value_type;
    typedef Ptr pointer;
    typedef Ref reference;
    typedef size_t size_type;
    typedef ptrdiff_t difference_type;
    typedef T** map_pointer;
    typedef __deque_iterator self;

    T* cur;
    T* first;
    T* last;
    map_pointer node;
    ...
};

start指向第一塊分區,finishi指向最後一塊分區,map是用來存放各個分區的地址(vector實現),map_size是map的大小。
start和finish做爲iterator,cur指向當前的元素,first指向第一個存放的元素,last指向當前分區中最後一個存放的數據以後的位置,node指回map。

deque 如何模擬連續空間?

基本所有依靠deque iterators完成

reference operator*() const
{
    return *cur;
}

pointer operator->() const
{
   return &(operator*());
}

difference_type operator-(const self& x) const
{
    return difference_type(buff_size()) * (node - x.node - 1) + (cur - first) + (x.last - x.cur);
}

self& operator++(){
    ++cur;
    if(cur == last){
        set_node(node + 1);
        cur = first;
    }
    return *this;
}

self operator++(int){
    self tmp = *this;
    ++*this;
    return tmp;
}

self& operator--(){
    if(cur == first){
        set_node(node - 1);
        cur = last;
    }
    --cur;
    return *this;
}

self operator--(int){
    self tmp = *this;
    --*this;
    return tmp;
}

void set_node(map_pointer new_node){
    node = new_node;
    first = *new_node;
    last = first + difference_type(buffer_size());
}

self& operator+=(difference_type n){
    difference_type offset = n + (cur - first);
    if(offset >= 0 && offset < difference_type(buffer_size())){
        cur += n;
    }
    else{
        difference_type node_offset = offset > 0 ? offset / difference_type(buffer_size()) : -difference_type((-offset - 1) / buffer_size()) - 1;
        set_node(node + node_offset);
        cur = first + (offset - node_offset * difference_type(buffer_size()));
    }
    return * this;
}

self operator+(difference_type n) const {
    self tmp = *thisl
    return tmp +=n;
}

self& operator-=(fifference_type n)
{
    return *this += -n;
}

self operator-(difference_type n) const
{
    self tmp = * this;
    return tmp -= n;
}

reference operator[](difference_type n) const
{
    return *(*this + n);
}
相關文章
相關標籤/搜索