Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's wrong with this class?

Tags:

c++

class

I think the problem is in main() but this compiles fine but I get no output. I think maybe it's not initalizing correctly because in debug mode it says
"myCharQ {item=0x0018fa00 "ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ̺yâpú" front=-858993460 rear=-858993460 ...}"

How would you rewrite this so that it is proper? I'm just starting out with classes so any help would be useful.

The following is a Array based Queue class

#include <iostream>
#include <cstdlib>
using namespace std;

const int MaxQueueSize = 10; // Queue Struct can hold up to 10 char.
typedef char ItemType; // the queue's data type is char

class CPPQueue
{
public:
    CPPQueue();
    ItemType item[MaxQueueSize];
    void initQueue(CPPQueue q);
    bool IsEmpty(CPPQueue q);
    bool IsFull(CPPQueue q);
    void Enqueue(CPPQueue q, ItemType newItem);
    void PrintQ(const CPPQueue q);
    void PrintQueueInfo(CPPQueue myQ);
    ItemType Dequeue(CPPQueue q);
private:
    int front, rear;
    int count;
};
CPPQueue::CPPQueue() 
{
    int front, rear, count = 0;
}
void CPPQueue::initQueue(CPPQueue q)
{
    q.front = q.rear = q.count = 0;
}
bool CPPQueue::IsEmpty(CPPQueue q)
{
    return (q.count == 0);
}
bool CPPQueue::IsFull(CPPQueue q)
{
    return (q.count == MaxQueueSize);
}
void CPPQueue::Enqueue(CPPQueue q, ItemType newItem)
{
    if(q.count == MaxQueueSize)
    {
        cerr << "Error! Queue is full, cannot enqueue item.\n" << endl;
        exit(1);
    }
    q.item[q.rear] = newItem;
    q.rear++;
    if (q.rear == MaxQueueSize)
    {
        q.rear = 0; // adjustment for circular queue
    }
    q.count++;
}
ItemType CPPQueue::Dequeue(CPPQueue q)
{
    ItemType theItem;
    if(q.count == 0)
    {
        cerr << "Error! Queue is empty, cannot dequeue item.\n" << endl;
        exit(1);
    }
    theItem = q.item[ q.front ];
    q.front++;
    if (q.front == MaxQueueSize)
    {
        q.front = 0; // adjustment for circular queue
    }
    q.count--;
    return theItem;
}
// Function PrintQ() prints the contents of the queue without changing
// the queue. Printing starts at the "front" index and stops before we
// get to the "rear" index. A decrementing counter controls the loop.
//
void CPPQueue::PrintQ(const CPPQueue q)
{
    int i;
    int qindex = q.front;
    for(i = q.count; i > 0; i--)
    {
        cout << q.item[qindex] ;
        qindex = (++qindex) % MaxQueueSize; // adjustment for circular queue
        if(i > 1)
            cout << ", ";
    }
}
// Helper function for the main program below.
void CPPQueue::PrintQueueInfo(CPPQueue myQ)
{
    cout << "The queue contains: ";
    PrintQ(myQ);
    cout << endl;
}
int main()
{
    CPPQueue myCharQ;// queue holds characters
    char ch; // char dequeued
    myCharQ.initQueue(myCharQ);
    myCharQ.Enqueue(myCharQ, 'a'); myCharQ.PrintQueueInfo(myCharQ);
    myCharQ.Enqueue(myCharQ, 'b'); myCharQ.PrintQueueInfo(myCharQ);
    myCharQ.Enqueue(myCharQ, 'c'); myCharQ.PrintQueueInfo(myCharQ);

    ch = myCharQ.Dequeue(myCharQ); myCharQ.PrintQueueInfo(myCharQ);
    ch = myCharQ.Dequeue(myCharQ); myCharQ.PrintQueueInfo(myCharQ);

    myCharQ.Enqueue(myCharQ, 'e');
    myCharQ.Enqueue(myCharQ, 'f'); myCharQ.PrintQueueInfo(myCharQ);
    myCharQ.Enqueue(myCharQ, 'g'); myCharQ.PrintQueueInfo(myCharQ);
    cout << endl;
    // print the dequeued characters
    while(!myCharQ.IsEmpty(myCharQ))
    {
        ch = myCharQ.Dequeue(myCharQ);
        cout << ch << " ";
    }
    cout << endl << endl;
    return 0;
}
like image 675
penu Avatar asked Dec 07 '25 08:12

penu


1 Answers

You never initialize the member variables front, rear, and count. You shadow them in your constructor by declaring variables with the same names again. Drop the int and just assign them (though this is not why the values aren't printed correctly, more on that in a bit). Actually, don't do that either; use an initializer list:

CPPQueue::CPPQueue() 
  : front(0), rear(0), count(0) 
{ }

Also, why do you have an initQueue function? You already have a constructor, rely on that to initialize your instance(s) (this is not C!).

Next, functions like IsEmpty are non-static member functions, yet they don't operate on the current instance. Don't take a queue as a parameter, just return if the instance is empty, full, whatever. Your code would have to be used like this:

Queue q;
q.IsEmpty(q);

Just strange. All of your member functions operate this way. When you cann a member function an implicit pointer to the current instance is passed as a hidden parameter (this). Therefore, each time the function is called it operates within the context of the instance it was called upon. You don't need to take an instance as a parameter.

Also realize that all of your functions take their arguments by value. You are going to be creating copies of these queues like crazy. If you modify the argument it will not be seen by the caller. For example:

void CPPQueue::initQueue(CPPQueue q)
{
    q.front = q.rear = q.count = 0;
}

That is essentially useless (aside from that fact that an initialize function is unnecessary). The changes to q.front, q.rear, and q.count will not be visible outside of that function as you are operating on a copy.

So even though your constructor is broken due to variable shadowing, this is why you still don't print what you expect to after calling initQueue. You are modifying a copy.

As for your implementation, it is not robust at all. You expose the underlying array to clients of your class. This is a bad idea. The items in a queue should not be directly accessible. What if I decide to muck with the array directly? Now all of your state variables are wrong. front, rear, and count are all potentially invalid as I have modified the state of the queue without going through any of your functions.

It's not even necessary; all I should be able to do is queue and dequeue items. That's it. That's what a queue does. It is not an array, if I want an array I will use one.

So, in summary, kudos on beginning to learn a relatively complex language. Keep at it and don't get discouraged, we all have to learn this stuff at some point.

EDIT: I have to run, but here is a quick rewrite of some of your class. I have removed your typedef for the item type. Why? It is unnecessary. You are not going to change it to another type per some platform or other environmental change, so the typedef only hurts the usability of your class. typedefs are good for things that may change (i.e., int32_t) for some environmental reason, but if they aren't helping you or clients of your code they are just one more thing to get in the way.

class CPPQueue
{
public:
    CPPQueue();
    bool IsEmpty() const;
    bool IsFull() const;
    void Enqueue(char newItem);
    char Dequeue();
    void PrintQ() const;
    void PrintQueueInfo() const;
private:
    char item[MaxQueueSize];
    int front
    int rear;
    int count;
};

CPPQueue::CPPQueue()
  : front(0), rear(0), count(0) { }

bool CPPQueue::IsEmpty() const
{ 
    // you don't actually need the this pointer
    // here, but I included it to make it clear
    // that you are accessing the count variable
    // for the current instance of a CPPQueue
    return this->count == 0; 
}

I hope this helps you rewrite the rest of your class, gotta go now. I added const in teh declaration of functions that should not mutate the internal state of a CPPQueue. Do a search for "const correctness" to get a better idea of why you would do such a thing. Good luck!

like image 168
Ed S. Avatar answered Dec 08 '25 22:12

Ed S.