自定義 STL list模板類 —— MyList 實現

MyList 實現

STL 中list 將元素按順序儲存在鏈表中,與 向量 相比,它允許快速的插入和刪除,但是隨機訪問卻比較慢。本文將實現簡單的 list功能。


頭文件及其命名空間

#include <iostream>
#include <memory>		// 內存配置器包含的頭文件   需要用到

using namespace std;

內存適配器可以點擊 C++11 —— allocator類瞭解,或者點擊 C++11 —— STL 存儲分配器簡單使用


定義結點

template <typename T>
struct __list_node
{
    typedef __list_node* list_node_pointer;

    list_node_pointer next;	// 後指針
    list_node_pointer prev;    	// 前指針
    T		      data;	// 存放數據
}
  1. 爲什麼用 typename T ,而不用 class T ? 因爲typename更加標準。主要是個人習慣 _
  2. typedef 取別名,方便記憶。
  3. list 模板類是一個雙向鏈表,有兩個指針分別指向前一個結點與後一個結點。

list 迭代器

迭代器用來操作容器

template <typename T>
struct __list_iterator
{
    // 迭代器本身
    typedef __list_iterator<T>	self;		// 迭代器本身
    typedef __list_node<T>* 	link_type;		// 結點指針  操作list

    // 只要一個結點指針,就可以操作整個 list
    link_type 			ptr; 

    __list_iterator(link_type p = nullptr) : ptr(p) {} 		// 構造數據
    __list_iterator(const self& that) : ptr(that.ptr) {} 	// 拷貝

    // 重載 == 
    bool operator==(const self& that)const
    {
        return this->ptr == that.ptr;
    }

    // 重載 !=
    bool operator!=(const self& that)const
    {
        // return !(operator==(that))
        return this->ptr != that.ptr;  
    }

    T& operator*()const
    {
        return ptr->data;		// 返回數據
    }
    
    // 迭代器前++ 指向下一個結點
    slf& operator++()
    {
        ptr = ptr->next;
        return *this;
    }
  
    // 後++ 先返回 再自增
    self operator++(int)	// 加個int 分開區別   避免報錯
    {
        self tmp = *this;
        ++*this;
        return tmp;
    }
	
    // -- 操作   與++操作差不多
    self& operator--()
    {
        ptr = ptr->prev;
        return *this;
    }
  
    self operator--(int)
    {
        self tmp = *this;
        --*this;
        return tmp;
    }
};
 迭代器的定義本身不難,不要把list 與迭代器混爲一起。
 此處我們的迭代器基本已經完成了。

MyList 類

簡單的定義list模板類中的幾種方法

template <typename T>
class MyList
{
protected:
    // 取一些別名  方便使用
    typedef __list_node<T>		list_node;	// 結點
    
    typedef T				value_tye;	// 值類型
    typedef value_type* 		pointer;	// T*
    typedef const value_type*		const_pointer	// const T*
    typedef size_t			size_type;
    
    // 空間適配器	分配內存
    /*
    大概實現
    	一級空間配置器	直接封裝了 molloc    free
   	 二級空間配置器	內存池   自有鏈表  (不需要知道具體是如何實現的)
    實現原理	
  	  用戶申請內存 > 128 ?   一級 :二級
    設計模式
  	  適配器模式	單例模式	   享元模式
    */

    // 分配一個 list_node大小的內存
    allocator<list_node>	nodeAllocator;

public:
    typedef list_node*		link_type;	// 指向結點的指針
    typedef __list_iterator<T>	iterator;	// 迭代器

protected:
    // 只需要一結點指針就可以表示整個 list
    link_type			node;
    
    // 空間配置器	內部會選擇更合適的方式
    // 分配新節點 (內存)	allocate
    link_type alloc_node()
    {
        return nodeAlloctor.allocate(sizeof(list_node));
    }

    // 釋放空間		不會析構結點 
    void dealloc_node(list_node p)
    {
        nodeAllocator.deallocate(p);
    }

    // 分配新節點(內存)用value 構造 新節點裏面內容
    link_type alloc_dtor_node(const T& value)
    {
        // 分配結點空間
        link_type p = alloc_node();
        // 構造內存
        nodeAllocator.construct(&p->data, value);

        return p;
    }
    
    // 析構結點	 釋放內存
    void dealloc_dtor_node(list_node p)   
    {
        nodeAlloator.detroy(&p->data);
        deallc_node(p);
    }

    // 空的時候初始化
    void empty_ini()
    {
        node = alloc_node();		// 分配一個結點空間 讓node 指向它
        // 只有一個結點的時候  指向自己   沒有元素值
        node->prev = node;
        node->next = node;
    }

public:
    MyList()
    {
        // list  初始化
        empty_init();
    }

    iterator begin()
    {
        return node->next;		// node
    }
    
    iterator begin()const		// 常迭代器
    {
        return node->next;
    }
    
    iterator end()			// end 在begin前一個 形成環狀的
    {
        return node;			// node->prev
    }

    iterator end()const
    {
        return node;
    }

    // 判空
    bool empty()const
    {
        return node == node->next;	// 判斷是不是等於本身
    }

    // 插入數據	   結點插入操作  畫圖可以理解的更快一點
    iterator insert(iterator pos, const T& value)
    {
        // 先創造一個結點
        link_type tmp = alloc_dtor_node(value);
        // 尋找位置 插入
        tmp->next = pos.ptr;
        tmp->prev = pos.ptr->prev;

        // 可以畫圖理解
        pos.ptr->prev->next = tmp;
        pos.ptr->prev = tmp;
        
        return pos;
    }

    // 屁股插入數據
    void push_back(const T& value)
    {
        insert(end(), value);		// 在尾部插入數據 
    }
};

插入結點:
插入結點
畫的有點醜 _


寫到這裏,我們就可以使用MyList 操作了

int main()
{
    MyList<int> mylist;

    // 從屁眼插入數據
    mylist.push_back(10);
    mylist.push_back(30);

    // 在指定位置插入數據
    mylist.insert(++mylist.begin(), 20);

    // 循環遍歷數據
    for(auto i = mylist.begin(); i != mylist.end(); i+++
    {
        cout << *i << endl;
    }
}

2021

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章