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; // 存放數據
}
- 爲什麼用 typename T ,而不用 class T ? 因爲typename更加標準。主要是個人習慣 _
- typedef 取別名,方便記憶。
- 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;
}
}