Dynamic Arrays using std::vector

Implementing a Circular Buffer with Vector

How do I implement a circular buffer using std::vector?

Abstract art representing computer programming

A circular buffer, also known as a ring buffer, is a fixed-size buffer that wraps around when it reaches its end. It's particularly useful for scenarios like buffering data streams or implementing a queue with a fixed maximum size. Let's implement a simple circular buffer using std::vector.

Basic Implementation

Here's a basic implementation of a circular buffer:

// CircularBuffer.h
#pragma once
#include <vector>
#include <stdexcept>

template <typename T>
class CircularBuffer {
public:
  CircularBuffer(size_t size)
    : buffer(size), max_size(size){}

  void push(const T& item){
    buffer[head] = item;
    if (full) { tail = (tail + 1) % max_size; }
    head = (head + 1) % max_size;
    full = head == tail;
  }

  T pop(){
    if (empty()) {
      throw std::runtime_error(
        "Buffer is empty");
    }
    T item = buffer[tail];
    full = false;
    tail = (tail + 1) % max_size;
    return item;
  }

  bool empty() const{
    return (!full && (head == tail));
  }

  size_t size() const{
    if (full) { return max_size; }
    if (head >= tail) { return head - tail; }
    return max_size + head - tail;
  }
  
  bool full = false;

private:
  std::vector<T> buffer;
  size_t head = 0;
  // Points to the next write position
  size_t tail = 0;
  // Points to the oldest element
  size_t max_size;
};

Using the Circular Buffer

Now let's see how we can use this circular buffer:

#include <iostream>
#include "CircularBufer.h"

int main(){
  CircularBuffer<int> buffer(5);

  // Fill the buffer
  for (int i = 1; i <= 5; ++i) {
    buffer.push(i);
  }

  std::cout << "Buffer full? "
    << (buffer.full ? "Yes" : "No") << '\n';
  std::cout << "Buffer size: " << buffer.size()
    << '\n';

  // Overwrite some values
  buffer.push(6);
  buffer.push(7);

  // Pop and print all values
  while (!buffer.empty()) {
    std::cout << buffer.pop() << ' ';
  }
  std::cout << '\n';

  std::cout << "Buffer empty? "
    << (buffer.empty() ? "Yes" : "No") << '\n';
}
Buffer full? Yes
Buffer size: 5
3 4 5 6 7
Buffer empty? Yes

In this example, we create a circular buffer of size 5. We fill it with numbers 1 to 5, then add 6 and 7, which overwrite 1 and 2. When we pop all elements, we get 3, 4, 5, 6, 7 in that order.

This implementation uses std::vector as the underlying container, which provides contiguous memory storage and efficient random access. The head and tail indices wrap around using the modulo operator, creating the circular behavior.

Remember, this is a basic implementation and could be extended with additional features like iterators, capacity changes, or thread safety, depending on your specific needs.

Answers to questions are automatically generated and may not have been reviewed.

3D art showing a progammer setting up a development environment
Part of the course:

Intro to C++ Programming

Become a software engineer with C++. Starting from the basics, we guide you step by step along the way

Free, unlimited access

This course includes:

  • 57 Lessons
  • Over 200 Quiz Questions
  • 95% Positive Reviews
  • Regularly Updated
  • Help and FAQ
Free, Unlimited Access

Professional C++

Comprehensive course covering advanced concepts, and how to use them on large-scale projects.

Screenshot from Warhammer: Total War
Screenshot from Tomb Raider
Screenshot from Jedi: Fallen Order
Contact|Privacy Policy|Terms of Use
Copyright © 2024 - All Rights Reserved