Improving C++ Encapsulation with the Pimpl Idiom: Listing 2

A message queue "pImpling" a primitive array.

/**********************************************
 my_queue.h
**********************************************/
#include <memory>
#include <string>

using namespace std;

typedef string element_t;

class my_queue {
  struct queue_impl; // forward-declared nested struct

  unique_ptr<queue_impl> pImpl;
public:
  my_queue();
  my_queue(const my_queue&);
  my_queue(my_queue&&);

  size_t enqueue(const element_t&);
  element_t dequeue();
  size_t get_lenght() const;
  bool is_empty() const;
  my_queue& operator=(const my_queue&);
  my_queue& operator=(my_queue&&);

  ~my_queue();
};



/**********************************************
 my_queue.cpp
**********************************************/
#include "my_queue.h"
#include <stdexcept>
#include <algorithm>
#include <utility>

using namespace std;

const size_t ARRAY_LIMIT = 1000;

struct my_queue::queue_impl {
  element_t* array;
  size_t top, bottom;

  queue_impl() : array{new element_t[ARRAY_LIMIT]}, top{0}, bottom{0} {}

  ~queue_impl() { delete[] array; }
};

my_queue::my_queue() : pImpl{new queue_impl{}} {}

my_queue::my_queue(const my_queue& other) : pImpl{new queue_impl{}} {
  for (size_t i = other.pImpl->top;i<other.pImpl->bottom;++i)
    enqueue(other.pImpl->array[i]);
}

my_queue::my_queue(my_queue&& other)=default;

size_t my_queue::enqueue(const element_t& e) {
  if (pImpl->bottom < ARRAY_LIMIT)
    pImpl->array[pImpl->bottom++] = e;
  else
    throw overflow_error("The queue ran out of space.");

  return pImpl->bottom;
}

element_t my_queue::dequeue() {
  if (pImpl->top == pImpl->bottom)
    throw runtime_error("The queue is empty.");

  if (pImpl->top > pImpl->bottom)
    throw runtime_error("The queue is an inconsistent state.");

  return pImpl->array[pImpl->top++];
}

size_t my_queue::get_lenght() const {
  return pImpl->bottom - pImpl->top;
}

bool my_queue::is_empty() const {
  return pImpl->top == pImpl->bottom;
}

my_queue& my_queue::operator=(const my_queue& other) {
  if (this!=&other) {
    pImpl->top = pImpl->bottom = 0;
    for (size_t i = other.pImpl->top;i<other.pImpl->bottom;++i)
      enqueue(other.pImpl->array[i]);

  }

  return *this;
}

my_queue& my_queue::operator=(my_queue&& other)=default;

my_queue::~my_queue()=default;



/**********************************************
 main.cpp
**********************************************/
#include "my_queue.h"
#include <iostream>

using namespace std;

int main(int argc, char* argv[]) {
  my_queue q1, q2;
  element_t lincoln_quote[] = {
    "Give", "me", "six", "hours", "to", "chop", "down", "a", "tree", "and",
    "I", "will", "spend", "the", "first", "four", "sharpening", "the", "axe"
  };

  for (int i = 0; i<10; ++i)
    q1.enqueue(lincoln_quote[i]);

  for (int i = 0; i<5; ++i) {
    cout << "Queue 1: " << (q1.dequeue()) << endl;
  }
  // prints
  // Queue 1: Give
  // Queue 1: me
  // Queue 1: six
  // Queue 1: hours
  // Queue 1: to

  q2 = q1;

  for (int i = 10; i<19; ++i)
    q1.enqueue(lincoln_quote[i]);

  while (!q2.is_empty()) {
    cout << "Queue 2: " << (q2.dequeue()) << endl;
  }
  // Queue 2: chop
  // Queue 2: down
  // Queue 2: a
  // Queue 2: tree
  // Queue 2: and

  while (!q1.is_empty()) {
    cout << "Queue 1: " << (q1.dequeue()) << endl;
  }
  // Queue 1: chop
  // Queue 1: down
  // Queue 1: a
  // Queue 1: tree
  // Queue 1: and
  // Queue 1: I
  // Queue 1: will
  // Queue 1: spend
  // Queue 1: the
  // Queue 1: first
  // Queue 1: four
  // Queue 1: sharpening
  // Queue 1: the
  // Queue 1: axe
}

About the Author

Diego Dagum is a software architect and developer with more than 20 years of experience. He can be reached at email@diegodagum.com.