Circular / Cyclic Buffer implementation I am learning circular / cyclic buffer using this link. Here is the code that I have written so far:

#include &lt;iostream&gt;
#include &lt;memory&gt;
#include &lt;cstddef&gt;

class CircularBuffer
{
    std::unique_ptr&lt;int[]&gt; u_ptr;
    std::size_t head;
    std::size_t tail;
    const std::size_t capacity;
    bool full_v;
public:
    CircularBuffer(std::size_t);
    void reset();
    bool full() const;
    bool empty() const;
    std::size_t size() const;
    void write(int);
    int read();
};

// Constructor
// Set the capacity via initializer list because it is const
CircularBuffer::CircularBuffer(std::size_t space):capacity{space}
{
    u_ptr = std::unique_ptr&lt;int[]&gt;(new int[space]);
    head = 0;
    tail = 0;
    full_v = false;
}

// Reset the Buffer
void CircularBuffer::reset()
{
    head = 0;
    tail = 0;
    full_v = false;
    u_ptr[head] = int{};
}

// Check if buffer is full
bool CircularBuffer::full() const
{
    return full_v;
}

// Check if buffer is empty
bool CircularBuffer::empty() const
{
    return (!full_v &amp;&amp; head == tail);
}

// Return the size of the buffer
std::size_t CircularBuffer::size() const
{
    if(full_v)
    {
        return capacity;
    }
    if(head &gt;= tail)
    {
        return head - tail;
    }
    else
    {
        return capacity - (tail - head);
    }
}

// Write values into the buffer
void CircularBuffer::write(int data)
{
    u_ptr[head] = data;
    head = (head + 1) % capacity;
    if(full_v)
    {
        tail = (tail + 1) % capacity;
    }
    full_v = (head == tail);
}

// Read from buffer
int CircularBuffer::read()
{
    if(this -&gt; empty())
    {
        return int{};
    }
    int ret_val = u_ptr[tail];
    full_v = false;
    tail = (tail + 1) % capacity;
    return ret_val;
}

int main()
{
    CircularBuffer cb{10};
    std::cout &lt;&lt; "Empty: " &lt;&lt; cb.empty() &lt;&lt; "\n";
    for(int i = 0; i &lt; 10; i++)
    {
        cb.write(i);
    }
    std::cout &lt;&lt; "Full: " &lt;&lt; cb.full() &lt;&lt; "\n";
    std::cout &lt;&lt; "Read: " &lt;&lt; cb.read() &lt;&lt; "\n";
    std::cout &lt;&lt; "Full: " &lt;&lt; cb.full() &lt;&lt; "\n";
    std::cout &lt;&lt; "Empty: " &lt;&lt; cb.empty() &lt;&lt; "\n";
    std::cout &lt;&lt; "Size: " &lt;&lt; cb.size() &lt;&lt; "\n";
    cb.write(35);
    std::cout &lt;&lt; "Size: " &lt;&lt; cb.size() &lt;&lt; "\n";
    std::cout &lt;&lt; "Read: " &lt;&lt; cb.read() &lt;&lt; "\n";
    std::cout &lt;&lt; "Size: " &lt;&lt; cb.size() &lt;&lt; "\n";
    cb.reset();
    std::cout &lt;&lt; "Size: " &lt;&lt; cb.size() &lt;&lt; "\n";
    return 0;
}



Is the implementation correct? What are the shortcomings that can be fixed?
I see that the original reference uses a mutex object and uses lock. Is such an approach used with all data structures? If not, is there a reason why that has been used here?
Your ringbuffer has a compile time size, so it would be appropriate to make it a template class rather than simply passing it as an argument to the constructor. You never plan to change it anyway?

template&lt;size_t bufSize&gt;
class CircularBuffer


That leads to the next point. You use a std::unique_ptr&lt;int[]&gt; There is something in C++ that is much better than this std::array. With the size of the buffer a compile time template argument of the class you can easily use it

template&lt;size_t bufSize&gt;
class CircularBuffer {
    std::array&lt;int, bufSize&gt; buf;
    std::size_t head;
    std::size_t tail;
    bool full_v;

    ...
};


Note that this increases the size of the class object considerably, as the std::array is directly inlined in the class.

The other members of this class are always the same after construction so you should use static member initialization:

template&lt;size_t bufSize&gt;
class CircularBuffer {
    std::array&lt;int, bufSize&gt; buf;
    std::size_t head{ 0 };
    std::size_t tail{ 0 };
    bool full_v{ false };

    ...
};


Note that this completely removes the need to define a constructor at all. The compiler generated default constructor will do just fine.

Resetting the buffer should not change the data stored in it as it is not read anyway so just leave it alone

void CircularBuffer::reset()
{
    head = 0;
    tail = 0;
    full_v = false;
}


Can tail ever be larger than head? If not why are you checking it.