在最近的項目中,我須要用到一個能設置固定長度的優先級隊列,查了一下知名的第三方庫,沒有找到合適的,因而,決定本身寫一個。node
須要的功能主要是:git
由於,我通讀過STL的源碼,對stl容器比較熟悉,寫一個相似的應該不難,節約時間。因此就仿照STL的list,寫了一個這樣的容器github
源碼地址:https://github.com/zfq559/ring_queueubuntu
首先想到的思路就是用循環數組,事先申請好固定長度的對象數組,循環使用,相似vector,可是,增刪的時候,須要移動元素,並且無法實現emplace函數了,寫到一半,這個思路就被廢棄了。因而,改用雙向鏈表,這樣,五個條件,都比較容易知足,可是,若是內存不是連續的話,遍歷的cache miss就比較高,因而就加入了內存池。數組
類定義爲:less
template <class T, uint32_t Size = 10, class Compare = std::less<T>>
class RingQueue;
其中,T爲容器中存放的數據類型,Size表示容器大小函數
元素節點定義成了這樣:性能
template <class T> struct QueueNode { char data[sizeof(T)]; QueueNode<T> *prev{nullptr}; QueueNode<T> *next{nullptr}; };
STL中,data的類型是一個 T*。我這裏之因此這樣定義,爲了只建立一個內存池對象,讓內存儘量連續。測試
插入元素使用的是插入排序:ui
Node *node = nullptr; for (node = dummy_->prev; node != dummy_; node = node->prev) { if (value_compare_(*(T *)node->data, value)) { break; } }
考慮到,使用的時候,更多狀況下,按順序插入元素,因此,這裏尋找插入點的時候,從後往前查找
判斷隊列是否滿,使用成員變量length_,若是插入一個元素以後,隊列超過Size大小了,刪除頭節點就能夠了,由於頭節點保存的是最小值:
// if queue is full, delete head node
if (length_ > Size) { Node *head = dummy_->next; _destroy((T *)head->data); // 析構節點中保存的對象 dummy_->next = head->next; // 刪除節點 head->next->prev = dummy_; length_--; alloc_.PutNode(head); // 內存回收 }
內存池也是相似STL中的分配器:
template <class T, uint32_t Num> class Allocator;
事先申請 (Num+2)*(sizeof(T)8字節對齊)的內存,每一個塊,稱爲一個 pool,內存池自動擴容,不過最多申請10個 pool。不能自動減小容量
使用 googletest 寫了十幾個用例,並和 std::list,std::set 做對比,在 win10/ubuntu18-x64/ubuntu16-armv8測試發現,這個容器性能是超過 std::set 的,更是超過了排序的 list
TODO:須要加入 benchmark 和 profile。增長erase函數,刪除指定節點
主要的特色就是上面所描述的,我的不免有考慮不到的地方,或者程序有bug,若是有人提出來,我很是感謝!