是的,我再来一次非常直接的实现,就像这样:
// write data always! if buffer is already full, overwrite old data!
void Put( const CONTENT_TYPE &data )
{
buffer[ inOffset++] = data;
inOffset%=size;
// was data overwritten, skip it by increment read offset
if ( inOffset == outOffset )
{
outOffset++;
outOffset%=size;
std::cout << "Overwrite" << std::endl;
}
}
CONTENT_TYPE Pull()
{
CONTENT_TYPE data = buffer[ outOffset++ ];
outOffset %= size;
return data;
}
但是这个简单的算法只使用缓冲区的1个大小的元素!
如果我想避免这种情况,我只找到一个添加另一个计数器变量的解决方案,这会浪费我sizeof(counter_var) - sizeof(element)bytes。
问:有没有浪费记忆的解决方案?它看起来很简单,但我无法理解: - )备注:还有一些代码行可以保护空读和其他内容,但这对于这个问题并不重要。并且它没有标记c ++,因为算法不依赖于语言,如果我给出一个c ++代码示例。
答案 0 :(得分:1)
如果一个是索引而另一个是元素计数,则可以使用两个整数并填充所有插槽,然后转换为动态查找第二个索引:
void put(const ELEMENT& element) {
if (nElements == size) throw "put: buffer full";
buffer[(start + nElements++) % size] = element;
}
ELEMENT get() {
if (nElements == 0) throw "get: buffer empty";
ELEMENT& value = buffer[start];
start = (start + 1) % size;
--nElements;
return value;
}
当然,如果您愿意,可以用if (foo > size) foo -= size;
替换mod操作。
答案 1 :(得分:0)
您只需使用不同的时间点来处理模数运算;假设我们在每次访问后增加读写指针。如果我们现在在增加后立即执行读指针的模数,并且在读取之前写指针的模数正好,则| write-read |完整缓冲区的长度是缓冲区的长度,没有任何特殊情况处理。为了实现这一点,您的写指针应始终使用 % buffer_length
,但存储 % (2 * buffer_length)
。
我并不特别喜欢马克的答案,因为将事物作为特殊情况处理通常不是一个好主意,因为引入负哨点值就像你通常使用size_t
的地方一样(即无符号整数)。
答案 2 :(得分:-1)
您可以为其中一个偏移使用特殊的sentinel值,例如-1,以指示缓冲区已满或为空。这将使您的代码复杂化,以检查和修改偏移量。
// write data always! if buffer is already full, overwrite old data!
void Put( const CONTENT_TYPE &data )
{
buffer[ inOffset++] = data;
inOffset%=size;
// was data overwritten, skip it by setting read offset to sentinel
if ( inOffset == outOffset || outOffset == -1 )
{
outOffset = -1;
std::cout << "Overwrite" << std::endl;
}
}
CONTENT_TYPE Pull()
{
if (outOffset == -1)
outOffset = inOffset;
CONTENT_TYPE data = buffer[ outOffset++ ];
outOffset %= size;
return data;
}
bool IsEmpty()
{
return outOffset == inOffset;
}