Boost.Circular_buffer
Boost.Circular_buffer维护了一块连续内存块作为缓存区,当缓存区内的数据存满时,继续存入数据就覆盖掉旧的数据。
它是一个与STL兼容的容器,类似于 std::list或std::deque,并且支持随机存取。circular_buffer
被特别设计为提供固定容量的存储大小。当其容量被用完时,新插入的元素会覆盖缓冲区头部或尾部(取决于使用何种插入操作)的元素。逻辑存储结构如图
boost::circular_buffer is on the way…
很多时候我们会用到缓冲区或者类似缓冲区的数据结构,如果能 事先估计出数据量多大,并尽可能的节约内存,可以使用环形(逻辑上)结构的缓冲区。boost已经有了一个这样的缓冲 区,circular_buffer,由Jan Gaspar设计实现,它的数据结构跟传统的静态环形双端队列(很多数据结构书上有相关介绍)一样,速度比传统的静态环形双端队列快得多。只不过我对它的 表现还是不太满意,觉得它还不够快。为此,我设计了一个简单的静态环形双端队列,它的数据结构与circular_buffer没什么两样,没有编写迭代 器,也没有给出太多公有成员函数,只不过它的速度要快一些。下面先来看看它的逻辑结构和物理结构图。
图(a)是静态环形双端队列的逻辑结构图,图(b)是对应的物理结构图。静态环形双端队列有四个指针,指针start指向分配内存块的起始地址 处,finish指向该快内存的末尾,first和last是两个自由指针,可以在[start, finish)区间自由移动,在队头添加一个元素时,first就向左移动一个元素的位置,在队头删除一个元素时,first就向右移动一个元素的位置, 在队尾添加一个元素时,last向右移动一个元素的位置,在队尾删除一个元素时,last向左移动一个元素的位置。
开始时,指针first和 last都指向同一个位置(我们的设计是指向[start, finish)区间的正中间),当环形双端队列满时last和first之间还剩一个元素的空位(如果不留空位怎么区分队满还是队空?)。比较难处理的问 题是指针first和last移动到队头和队尾怎么办,因为在物理上(在内存中存放元素时)一块存储空间的始末永远都不会构成一个环,环行结构只能在逻辑 上出现。
解决此问题的一种方法是取模,例如:first向左移动一个元素的位置:first = (first - 1 + cap) % cap; last向右移动一个元素的位置:last = (last + 1) % cap;其中first和last都是整形变量,cap是所开辟空间的大小,这就能很好的解决了环形移动指针的问题。这种传统的算法形式上虽然比较简洁, 但是速度慢,因为取模需要做除法运算,以现在的CPU架构,做一次除法相当于做多次加法。
另外一种解决方案更容易理解,例如:当指针first移 动到最左端时就让它指向右端,移动到最右端时就让它指向左端,当指针last移动到最右端时就让它指向左端,移动到最左端时就让它指向右端,这种算法速度 快,因为做一次判断需要的指令数跟做一次加法需要的指令数相差不多。
为了能够区分何时队满何时队空,环形队列应该至少富裕一个元素的空间,也就是说我们将用last + 1 == first表示队满,用last == first表示队空。