1. 程式人生 > >STL list原始碼分析以及實現

STL list原始碼分析以及實現

本文主要內容如下:
1. STL list實現的三個模組節點__list_node迭代器__list_iterator以及list本身(使用一個__list_node*代表整個連結串列)的介紹。
2. 重點分析list的幾個核心函式,理解STL list的實現原理,核心函式如下:
  • list 的建構函式

  • 基本的迭代器操作

  • 插入操作

  • size, 解構函式等

3. 測試實現的基本的list,包括基本型別如int以及結構體。

1. STL list實現的三個模組

1.1 STL list節點

節點定義如下:

template<typename
T> struct __list_node{ typedef __list_node* list_node_pointer; list_node_pointer prev; list_node_pointer next; T data; };
1.2 迭代器__list_iterator

迭代器的定義如下(下面先省略了iterator_category迭代器型別),迭代器主要是作為 list內部的iterator來使用:

template<typename T>
struct __list_iterator{
    typedef
__list_iterator<T> self; typedef __list_node<T>* link_type; link_type ptr; //成員 __list_iterator(link_type p = nullptr):ptr(p){} .....//先省略成員函式 }

list的迭代器需要實現的操作有:++、–、*、->、==、!=,定義如下:

T& operator *(){return ptr->data;}

T* operator ->(){return &(operator
*());} self& operator++(){ ptr = ptr->next; return *this; } self operator++(int){ self tmp = *this; ++*this; return tmp; } self& operator--(){ ptr = ptr->prev; return *this; } self operator--(int){ self tmp = *this; --*this; return tmp; } bool operator==(const __list_iterator& rhs){ return ptr == rhs.ptr; } bool operator!=(const __list_iterator& rhs){ return !(*this==rhs); }

2. list的核心實現

2.1 list節點的主要型別,以及成員變數

list主要的變數別名,以及成員定義如下:

    template<typename T>
    class SimpleList{
        protected:
            typedef __list_node<T> list_node;
            // nodeAllocator 按照 list_node為單位分配記憶體
            typedef allocator<list_node> nodeAllocator;

        public:
            typedef T                  value_type;
            typedef T&                 reference;
            typedef value_type*        pointer;
            typedef list_node*         link_type;
            typedef const value_type*  const_pointer;

            typedef size_t             size_type;

        public:
            typedef __list_iterator<value_type> iterator;

        private:
            link_type node; // 只要一個指標,便可表示整個環狀雙向連結串列

       .............//為了更清晰的看list的定義,先省略其他的函式
    }
2.2 list的建構函式
在給出list的建構函式之前,先給出,list內部的向空間配置申請節點的記憶體分配,以及在節點上面構造物件(呼叫物件的建構函式),節點返還給空間配置器,以及物件的析構(呼叫物件的解構函式)。
// 分配一個新結點(分配記憶體), 注意這裡並不進行構造,
link_type alloc_node(){
    return nodeAllocator::allocate();
}

// 釋放一個結點(節點記憶體由空間配置回收)
void dealloc_node(link_type p){
    nodeAllocator::deallocate(p);
}

// 產生(配置並構造)一個節點, 首先分配記憶體, 然後進行構造
link_type alloc_ctor_node(const T& val){
    link_type p = alloc_node();
    // 這裡要構造的是節點的data
    construct(&p->data, val);
    return p;
}

// 析構結點元素, 並釋放記憶體
void dealloc_dtor_node(link_type p){
    destroy(&p->data);
    dealloc_node(p);
}

基本的建構函式定義如下:

void empty_initialize(){
    node = alloc_node(); // 配置一個節點空間,令node指向它
    node->prev = node;   // 令node頭尾都指向自己,不設元素值
    node->next = node;
}

SimpleList(){
    empty_initialize();
}

注:explicit SimpleList(size_t n);建構函式後面在給出

2.3 list迭代器的基本操作

iterator begin(){
    // link_type可以轉化為iterator(建構函式)
    // iterator(過載了++ --等)
    return (link_type)(node->next);
}

iterator begin()const{
    return (link_type)(node->next);
}

iterator end(){
    // 連結串列成環, 當指所以頭節點也就是end
    return node;
}

iterator end()const{
    return node;
}

empty判斷:

bool empty()const{return node == node->next;}
2.4 list的插入操作
template<typename T>
typename SimpleList<T>::iterator SimpleList<T>::insert(
        iterator position, const T& value){

    link_type tmp = alloc_ctor_node(value);
    // 調整雙向指標,使tmp插入進去
    tmp->next = position.ptr;
    tmp->prev = position.ptr->prev;

    position.ptr->prev->next = tmp;
    position.ptr->prev = tmp;

    return tmp;
}

template<typename T>
void SimpleList<T>::push_back(const T& value){
    insert(end(), value);
}

    template<typename T>
    typename SimpleList<T>::iterator SimpleList<T>::insert(
            iterator position, size_t n, const T& value){

        while(n--){
            // 由於是相同的值,所以順序無關
            insert(position, value);
        }
    }

接下來我們看 explicit SimpleList(size_t n);建構函式

template<typename T>
void SimpleList<T>::fill_initialize(size_t n, const T& value){
    // 先初始化起始點
    empty_initialize();

    insert(begin(), n, value);
}

template<typename T>
SimpleList<T>::SimpleList(size_type n, const T& value){
    fill_initialize(n, value);
}

如下這個建構函式有點小問題,會建立一個臨時物件,然後呼叫物件的copy建構函式,實際上STL 中的list,只會呼叫物件的預設建構函式,這裡只是為了簡化,具體的可以見前面的vector原始碼實現分析文章。

template<typename T>
SimpleList<T>::SimpleList(size_t n){
    fill_initialize(n, T());
}

3. 測試實現的基本的list,包括基本型別如int以及結構體。

3.1 基本型別int測試

首先測試基本的int

SimpleList<int> slInt(5, 10);
std::cout<<slInt<<std::endl;
std::cout<<slInt.size()<<std::endl; // 8

輸出如下:

這裡寫圖片描述

注意:
1. allocator分配的大小是以__list_node為大小單位,初始化的時空間配置器,沒有任何記憶體,由於申請的大小單位<128bytes,因此二級配置器,會給對應的free_list分配20個節點。
2. 建構函式根據大小n每次拿一個

接下來:

SimpleList<int> slInt1;
std::cout<<slInt1.size()<<std::endl; // 0

輸出如下:

這裡寫圖片描述

會預設的為list構造一個頭節點。

3.2 結構體測試

結構體測試,結構體如下:

struct TestLst{
    int a;   char c1;
    TestLst(int i = 0, char c = 'c'):a(i), c1(c){
        std::cout<<"TestLst ctor  a: "<<a<<" c1: "<<c1<<std::endl;
    }

    TestLst(const TestLst& tv):a(tv.a), c1(tv.c1){
        std::cout<<"TestLst copy ctor a: "<<a<<" c1: "<<c1<<std::endl;
    }

    ~TestLst(){
        std::cout<<"TestLst dtor a: "<<a<<" c1: "<<c1<<std::endl;
    }
};

測試程式碼如下:

SimpleList<TestLst> slStr(5);
std::cout<<"======now clear======"<<std::endl;
slStr.clear();
std::cout<<"======now insert new item======"<<std::endl;
slStr.push_back(TestLst());

輸出如下:

這裡寫圖片描述