在最近的项目中,我须要用到一个能设置固定长度的优先级队列,查了一下知名的第三方库,没有找到合适的,因而,决定本身写一个。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,若是有人提出来,我很是感谢!