This is the generic version of the Circular Buffer I have posted before. You can find the non-generic version of the circular buffer here
The header file:
CircularBuffer.hpp
The header file:
CircularBuffer.hpp
#ifndef MCP_CIRCULAR_BUFFER_GENERIC_H_ #define MCP_CIRCULAR_BUFFER_GENERIC_H_ template<class T> class CircularBuffer { public: // Creates a buffer with 'slots' slots. explicit CircularBuffer(int slots); // Destructor. ~CircularBuffer(); // Writes 'value' to the next available slot. It may overwrite // values that were not yet read out of the buffer. void write(const T & value); // Returns the next value available for reading, in the order they // were written, and marks slot as read. // If the buffer is empty returns -1. void read(T* val); // Removes all the elements from the buffer. void clear(); // returns true if the Circular buffer is empty, false otherwise bool isEmpty(); private: //array of integers T* data_; // the size of the buffer int num_of_slots_; //index to read the next integer from buffer int read_index_; //index to write a new integer to buffer int write_index_; // Non-copyable, non-assignable. CircularBuffer(CircularBuffer&); CircularBuffer& operator=(const CircularBuffer&); }; template <class T> CircularBuffer<T>::CircularBuffer(int slots) { if (slots <= 0) { num_of_slots_ = 10; /*pre-assigned value */ } else { num_of_slots_ = slots; } clear(); } template <class T> CircularBuffer<T>::~CircularBuffer() { delete[] data_; } template <class T> void CircularBuffer<T>::write(const T & value) { data_[write_index_] = value; if (read_index_ == -1) { //if buffer is empty, set the read index to the //current write index. because that will be the first //slot to be read later. read_index_ = write_index_; } write_index_ = (write_index_ + 1) % num_of_slots_; } template <class T> void CircularBuffer<T>::read(T* val) { if (read_index_ != -1) { // if buffer is not empty *val = data_[read_index_]; read_index_ = (read_index_ + 1) % num_of_slots_; if (read_index_ == write_index_) { /*all available data is read, now buffer is empty*/ read_index_ = -1; } } } template <class T> void CircularBuffer<T>::clear() { read_index_ = -1; /* buffer empty */ write_index_ = 0; /* first time writing to that buffer*/ delete[] data_; data_ = new T[num_of_slots_]; /* allocate space for buffer */ } template <class T> bool CircularBuffer<T>::isEmpty() { return read_index_ == -1; } #endif // MCP_CIRCULAR_BUFFER_GENERIC_H_
Sample main file main.cpp
#include <iostream> #include "include/CircularBuffer.hpp" using namespace std; int main() { CircularBuffer<unsigned int> cb(5); unsigned int val; for (unsigned int i=1;i<=15;i++) cb.write(i); while(!cb.isEmpty()) { cb.read(&val); cout << val << endl; } return 0; }
Thanks for the example!
ReplyDeleteYou should update the code of the destructor. The data_ was created as an array and should therefor be deleted as an array delete[] data_;
you are welcome and thank you :)
DeleteAs far as I can see the member function 'clear()' is public, therefore is aimed to provide a way for user of your class to delete all the content of buffer. But don't you think the way you implement leak memory, since you are allocating new buffer (on 'clear()') without 'delete' on the previous buffer. I think each time clear gets called you will leak (number_of_slots * sizeof(T)) bytes(except the first time when called within constructor).
ReplyDeleteWhy don't you create the buffer directly in constructor?
If you want to use a new buffer for 'clear()', you can still delete the previous buffer (hence returning the memory to OS) and create another one inside 'clear()'?
Or, much better solution might be usage of std::vector or (in case of C++11/14) std::unique_ptr??
I am not an expert in C++, so I might be wrong :)
You are right. thank you for the suggestion :)
Delete