Implementing a queue in C++ I'm trying to implement a really basic queue (as an array) in my computer science class and want some feedback on my logic/methodology.  the queue is not circular, it just includes the basic functions of enqueue/dequeue (along with the other obvious ones.) Please criticize and give feedback so I can educate myself on the best practices. 

I was given this assignment as part of an entry level CS class. I was told not to make a circular queue. So this of course is not an efficient model by any means.. just a rough idea of what queues do. 

what the code should do: users can add to the queue by moving forward the last element of the array, and "remove" items (they kinda still float around in memory though. remove isn't a great word) by moving the first element forward. 

Class declaration:

class queueList
{
private:
    int *list;
    int queueFront;
    int queueRear;
    int maxSize;

public:
    void enqueue(const int &amp;x);
    bool isEmpty();
    bool isFull();
    void dequeue();
    queueList(const int &amp;x);
    int front();
    int back();
};



class implementation: 

queueList::queueList(const int &amp;x)
{
    list = new int[x];
    queueRear = 0;
    queueFront = 0;
    maxSize = x;
}
void queueList::enqueue(const int &amp;x)
{
    if (!isFull())
    {
        list[queueRear] = x;
        //cout &lt;&lt; list[queueRear] &lt;&lt; endl;
        queueRear++;
    }
}
bool queueList::isEmpty()
{
    return (queueFront == queueRear);
}
bool queueList::isFull()
{
    return (queueRear == maxSize);
}

void queueList::dequeue()
{
    if (!isEmpty())
    {
        list[queueFront] = 0;
        cout &lt;&lt; list[queueFront] &lt;&lt; endl;
        queueFront++;
    }
}

int queueList::front()
{
    return list[queueFront];
}

int queueList::back()
{
    return list[queueRear];
}
```
Use std::size_t for container sizes and indices, not int.

std::size_t queueFront;
std::size_t queueRear;
std::size_t maxSize;


Use meaningful variable names. When I use your constructor, what does "x" mean? Something like queue_size is more meaningful. This can also be a std::size_t. This also doesn't need to be a reference, since it's likely the same size as a pointer, but feel free to continue passing as reference if you want. You just normally don't see that with primitive types.

queueList(std::size_t queue_size);


Similarly, pick a meaningful name for your enqueue function. Even something like value carries more meaning than x. This should NOT be a std::size_t, since obviously you do want to insert an int into your int array.

Handle the special case in your constructor where the passed-in size is 0 or less. It won't be less than 0 if you switch to size_t, but it can still be 0. Do you want to allow this? How do you manage the memory? Calling new int[0]; is undefined behavior. Also, use initializer lists when possible for constructors. It prevents variables from being double-assigned and objects from being double-constructed. You won't notice any speedup on this problem, but it's a good habit to get into.

queueList::queueList(std::size_t queue_size)
  : list(queue_size &gt; 0 ? new int[queue_size] : nullptr)
  , queueRear(0)
  , queueFront(0)
  , maxSize(queue_size)
{ }


Explicitly delete your default constructor to avoid ambiguity.

queueList() = delete;


Your constructor manages a resource (memory allocated with new), so you must also declare a destructor that deletes the memory. Since you must declare a destructor, you must follow at LEAST the rule of 3 and define a copy constructor and a copy assignment operator. A best practice would be to follow the rule of 5 (same link as rule of 3) and define additionally a move constructor and move assignment operator.

They key with all of these is: what does it mean to create a copy of your queue? What does it mean to move your queue? Most of these questions will have to do with managing the underlying allocated memory.

~queueList() { delete[] list }; // destructor
queueList(const queueList&amp; other); // copy constructor
queueList&amp; operator=(const queueList&amp; other); // copy assignment operator
queueList(queueList&amp;&amp; other); // move constructor
queueList&amp; operator=(queueList&amp;&amp; other); // move assignment operator


Your queue is currently one-time-use. It can only ever hold queue_size (x in your code) elements ever. It can't hold more elements than what you initially allocate. The latter is surmountable, but you should make it a goal to allow the queue to grow (or have a policy of throwing some error, or letting the user know through documentation that excess values will be lost). The one-time usability of each data slot is CRIMINAL though. My suggestion is to allow queueRear and queueFront to wrap around the array using modulo arithmetic as they move to the right.

dequeue should return a value!!! This is a huge shock to anyone who will try to use your class. They don't always want to print the value. They likely want to use it in some future calculation. This is easy. However, your current code doesn't even print the current value. It just always overwrites it with 0, then prints 0. This also decouples your class from requiring iostream to work.

Change your dequeue to look like this:

int queueList::dequeue()
{
    if (isEmpty())
    {
        // figure out what to do here.
        // Throw an error? Seems appropriate. 
        // Another option is to return a std::pair&lt;int, bool&gt;,
        // where the second value indicates whether there were
        // any items in the queue
    }
    int retval = list[queueFront++];
    queueFront = queueFront % maxSize; // talking about modulo arithmetic earlier
    return retval;
}


Your isEmpty() and isFull() functions can have their prototypes modified to be const, since neither modify the object:

bool isEmpty() const
{
    ...
}

bool isFull() const
{
    ...
}