C++ C++;简单循环缓冲队列

C++ C++;简单循环缓冲队列,c++,queue,C++,Queue,我使用数组实现了一个队列,并将其视为循环缓冲区\u tail指向下一个要读取的元素,\u head指向下一个要写入的元素: template<int SIZE> class Q { bool push(const int item){ if(false == isFull()){ _q[_head] = item; _head = ++_head % SIZE; return true; } return f

我使用数组实现了一个队列,并将其视为循环缓冲区
\u tail
指向下一个要读取的元素,
\u head
指向下一个要写入的元素:

template<int SIZE>
class Q {

bool push(const int item){
    if(false == isFull()){
        _q[_head] = item;
        _head = ++_head % SIZE;
        return true;
    }

    return false;
}

bool pop(int& toReturn){
    if(false == isEmpty()){
        toReturn = _q[_tail];
        _tail = ++_tail % SIZE;  
        return true;
    }

    return false;
}

private:

    std::array<int, SIZE> _q;
    std::atomic<int> _head;
    std::atomic<int> _tail;
};
模板
Q类{
布尔推送(常量整数项){
如果(false==isFull()){
_q[_head]=项目;
_头部=++\头部大小百分比;
返回true;
}
返回false;
}
布尔波普(内翻和内翻){
if(false==isEmpty()){
toReturn=_q[_tail];
_尾=++_尾%大小;
返回true;
}
返回false;
}
私人:
std::数组q;
标准:原子头;
std::原子尾;
};
但是,我的
isFull()
isEmpty()
逻辑中有一个bug,由以下问题确定:

当队列最初为空时,
\u tail
\u head
都将指向
\u q[0]

当我写了
\u q[0]
\u q[1]
\u q[2]
q[3]
之后,再次
\u tail
\u head
将指向同一个,因此显然我不能使用
\u tail
=
\u head
来确定满/空


实现
isEmpty()
isFull()逻辑的最简单方法是什么?我不确定是否应该在读取队列项目后写入“空白”枚举?

在类中声明一个数据成员(计数器),该类统计队列中的项目数。递增(按)和递减(按)。当它为(0)时,队列为空

如果您愿意将队列的有效大小减少一个元素,则逻辑相对简单:

  • head
    tail
    相同时,队列为空
  • tail
    增加1时,使用环绕,产生
    head
    ,队列已满
这使得不可能插入第N个元素,作为“哨兵”。因此,对于一个N元素队列,您需要分配一个N+1元素的数组,并在所有处理环绕的表达式中使用
(SIZE+1)

std::array<int,SIZE+1> _q;
具有未定义的行为,因为编译器可以灵活地应用在赋值结束之前或之后递增
\u head
\u tail
的副作用。如果编译器选择在赋值后应用副作用,则不会发生环绕效应

由于您根本不需要复合赋值,因此修复非常简单:

_head = (_head+1) % SIZE;
_tail = (_tail+1) % SIZE;  

将实现更改为类似的方式如何

将头的定义从“
\u头指向下一个要写入的元素:
”更改为“
\u头指向最新添加的元素”

然后可以使用
isFull()

bool isFull()
{
    if((_head + 1) % SIZE == _tail)
        return true;

    return false;
}
isEmpty
可能是

bool isEmpty()
{
    if(_head == _tail)
        return true;

    return false;
}
pop()
将是相同的,但更改为
push()
,不要忘记将
\u head
\u tail
初始化为
-1

bool push(const int item){
    if(false == isFull()){
        if(_head == -1)    //If adding the first element,
            _tail = 0;     // tail will point to it as it was inititiallty 0
        _head = ++_head % SIZE;
         _q[_head] = item;
        return true;
    }

    return false;
}

由于您使用模来计算数组的索引,因此为每次读写保留一个运行计数可能会简化逻辑

像这样更改实现

bool push(const int item){
    if(false == isFull()){
        _q[_head % SIZE] = item;
        ++_head;
        return true;
    }

    return false;
}

bool pop(int& toReturn){
    if(false == isEmpty()){
        toReturn = _q[_tail % SIZE];
        ++_tail;  
        return true;
    }

    return false;
}
用这些

bool isFull()
{
    return _head - _tail == SIZE;
}

bool isEmpty()
{
    if (_head == _tail)
    {
        _head = _tail = 0;
        return true;
    }

    return false;
}

我发现用
\u head=\u tail=-1
来表示空虚是最简单的。您不必保留额外的变量,其余的逻辑也保持不变。
bool isFull()
{
    return _head - _tail == SIZE;
}

bool isEmpty()
{
    if (_head == _tail)
    {
        _head = _tail = 0;
        return true;
    }

    return false;
}