I am trying program a queue with arrays in C++.
I used this approach https://stackoverflow.com/a/936709/7104310 as shown below.
My question: How can I index the arrays to fill them?
In a normal 2d-array it would be arr[3][2] for example. But I do not know how to do this with pointers. The question hat not been answered in the Solution upon.
Thank you!
#include <iostream>
#define MAX_SIZE 3
using namespace std;
// ary[i][j] is then rewritten as
//arr[rear*capacity + front]
// Class for queue
class msg_queue
{
char **arr; // array to store queue elements
int capacity; // maximum capacity of the queue
int front; // front points to front element in the queue (if any)
int rear; // rear points to last element in the queue
int count; // current size of the queue
public:
msg_queue(int size = MAX_SIZE, int slot_length = MAX_SIZE); // constructor
void dequeue();
void enqueue(char x);
char peek();
int size();
bool isEmpty();
bool isFull();
};
// Constructor to initialize queue
msg_queue::msg_queue(int size, int slot_length)
{
arr = new char*[size];
for (int i = 0; i < size; ++i) {
arr[i] = new char[slot_length];
}
capacity = size;
front = 0;
rear = -1;
count = 0;
}
// Utility function to remove front element from the queue
void msg_queue::dequeue()
{
// check for queue underflow
if (isEmpty())
{
cout << "UnderFlow\nProgram Terminated\n";
exit(EXIT_FAILURE);
}
cout << "Removing " << arr[front] << '\n';
front = (front + 1) % capacity;
count--;
}
// Utility function to add an item to the queue
void msg_queue::enqueue(char item)
{
// check for queue overflow
if (isFull())
{
cout << "OverFlow\nProgram Terminated\n";
exit(EXIT_FAILURE);
}
cout << "Inserting " << item << '\n';
rear = (rear + 1) % capacity;
arr[rear] = item; //ERROR HERE
count++;
}
// Utility function to return front element in the queue
char msg_queue::peek()
{
if (isEmpty())
{
cout << "UnderFlow\nProgram Terminated\n";
exit(EXIT_FAILURE);
}
return arr[front]; //ERROR HERE
}
Well, it's still arr[3][2].
Although arrays are not pointers, the way we use them is effectively using a pointer because of the way they work and the way their name decays.
x[y] is *(x+y), by definition.
That being said, I would recommend you drop the 2D dynamic allocation (which is poison for your cache) and create one big block of Width×Height chars instead. You can use a little bit of maths to provide 2D indexes over that data.
Also you forgot to free any of that memory. If you use a nice std::vector to implement my suggested 1D data scheme (or even if you hire a vector of vectors, but ew!) then it'll be destroyed for you. Of course if you could do that then you'd probably be using std::queue…
Related
I am trying to implement a circular queue.
I have declared size of the queue in the header file and I initiated my queue using size variable via constructor.
Here are queue.h and queue.cpp files.
class Queue
{
public:
int size;
int front, rear;
int A[];
Queue(int size);
bool isEmpty();
void enqueue(int n);
int dequeue();
int Peek();
void Display();
int sizeQ();
};
Here is queue.cpp
Queue::Queue(int size)
{
int A[size];
front = rear = -1;
}
bool Queue::isEmpty(){
if((front == -1) && (rear == -1))
return true;
else
return false;
}
void Queue::Display(){
if(isEmpty()){
cout << "Its empty! Nothing to display"<<endl;
}else{
for(int i=0; i<sizeQ(); i++){
cout << A[i] << endl;
}
}
cout <<endl;
}
Here is my main
int main()
{
Queue q1(10);
q1.enqueue(20);
q1.Display();
return 0;
}
The problem: Loop inside display function does not see the size variable although I created object using size inside main. When I debug the program, I saw that size is 0, thus for loop never starts.
What I tried
int Queue::sizeQ(){
return size;
}
I tried to return size via method; however, no luck. What should I do in order to access size variable?
Currently your constructor creates a local array that gets destroyed after it completes. You don't want to do this.
If you want to set the size of an array at run time it has to be declared on the heap. To do that you should change the declaration of the array A like this in the header:
int *A;
Then in your constructor you can allocate the array on the heap:
Queue::Queue(int iSize):
size(iSize), front(-1), rear(-1)
{
A = new int[size];
}
Note the initialiser list is initialising member variables size, front and rear.
You must also deallocate your array. To do this add a destructor to your class Queue and do this:
Queue::~Queue()
{
delete [] A;
}
This will free up the memory used by A.
Queue::Queue(int size)
{
int A[size];
front = rear = -1;
}
You never initialize this->size here. Hence sizeQ() returns uninitialized value of size member.
Add this->size = size; inside the constructor.
EDIT: the int A[size] does not do what you think it does. It is creating a local array and has nothing to do with the member A. Refer to #jignatius answer to see how to fix it.
Initiate size inside constructor like below:
Queue::Queue(int nSize) //changed name of parameter to nSize to remove confusion
{
int A[size];
front = rear = -1;
size = nSize; // Initialize passed param to member variable of class
}
I have a queue class where I implement the queue structure.
#include "queue.h"
Queue::Queue()
{
}
Queue::Queue(int size){
front = rear = -1;
this->size = size;
Q = new int[size];
}
void Queue::enqueue(int x){
if (rear == size -1 ){
cout << " its full" << endl;
}else{
rear ++;
Q[rear] = x;
}
}
int Queue::dequeue(){
int x= -1;
if (rear == front){
cout << " its empty"<<endl;
}else{
front ++;
x = Q[front];
}
return x;
}
void Queue::Display(){
for(int i= front+1; i<=rear; i++){
cout << Q[i] << " ";
}
cout << endl;
}
bool Queue::isEmpty(){
return (size==0);
}
int Queue::peek()
{
if (isEmpty())
{
cout << "UnderFlow\nProgram Terminated\n";
exit(EXIT_FAILURE);
}
return Q[front];
}
In main.cpp, I create multiple queues. I am trying to implement a scheduling algorithm where I need to process each queue in order. The problem starts when I try to go through each queue. I would like to use only one for loop to access the element of each queue rather than for loop for each of them.
Example:
queue[1..N] where N is the number of queues. In for loop, I want to check if queue[i].empty().
I found a solution to the problem. In the main.cpp, following code solved the issue.
Queue allQueues[4];
allQueues[0] = queue1;
allQueues[1] = queue2;
allQueues[2] = queue3;
allQueues[3] = queue4;
To access:
for(int i=0; i<4; i++){
if allQueues[i].empty(){
//do something
}
}
If you need to generate a specific number of instances of your Queue class that is fixed and known at compile time, your code solution will work. However, if you have a program where new Queue instances need to be created while the program is running, you need to use dynamic memory allocation on the heap.
One approach to this is to create an array or a vector of pointers to your Queue class in main.cpp. The std::vector is more flexible, and it's best to use a smart pointer to create each instance of Queue, although many academic courses won't allow use of the standard template library or of smart pointers, and in that case you need just a normal array of pointers to Queue and use new and delete appropriately.
const int SIZE = 100 //max number of Queue instances
Queue* allQueues[SIZE]; //array of uninitialized pointers to Queue
for (int i = 0; i < SIZE; i++) { //ensure all pointers are set to null
allQueues[i] = nullptr;
}
//To make a new Queue instance and insert it into the array:
allQueues[0] = new Queue();
//And when done with that Queue instance, to avoid memory leaks and dangling pointers:
delete allQueues[0];
allQueues[0] = nullptr;
(This is all much better done with std::array, or std::vector, and smart pointers).
Note also the memory usage, without this approach you have two full-sized instances of Queue for queue1, instead of the object itself and a pointer to that object. However, one can do the array of pointers thing using only automatic stack allocation as well, but in that case, you don't want to be creating new objects at runtime. For that, it's simple:
Queue* allQueues[4];
allQueues[0] = &queue1;
//etc.
P.S. One problem with your solution is that when you do this assignment:
allQueues[0] = queue1;
You need a copy constructor in your class, or an overloaded '=' operator, to ensure that all of queue1's internals are correctly copied over into the the array of Queue objects, and avoid all the 'shallow copy' issues.
Queue::Queue(const Queue& copySource) {
this->size = copysource.size;
this->Q = new int[copysource.size];
for (int i = 0; i < size; i++) {
this->Q[i] = copysource.Q[i];
}
See:
Why can I access private variables in the copy constructor?
I have a priority queue array that is filled with "Jobs" (name + priority). I've been able to get everything queue related working aside from re sizing if it is full. Here is the bits that I think are causing a segmentation fault that I haven't been able to figure out.
EDIT:
Here is a bit more code that will compile, I left in the rest of the functions in case those might help in any way. Right now the initial capacity is set to 5, when you try to add a job to the full list it will double the capacity of the array and allow you to add a couple more jobs before a SEG. fault.
pq.h
#ifndef PQ_H
#define PQ_H
#include "interface.h"
#include <string>
using namespace std;
class Job {
public:
int getPriority();
string getTaskName();
void setPriority(int val);
void setTaskName(string tname);
Job();
private:
int priority;
string taskName;
};
class PriorityQueue {
public:
PriorityQueue();
~PriorityQueue();
int size();
bool isEmpty();
void clear();
void enqueue(string value, int priority);
string dequeue();
string peek();
int peekPriority();
PriorityQueue(const PriorityQueue & src);
PriorityQueue & operator=(const PriorityQueue & src);
private:
static const int INITIAL_CAPACITY = 5;
Job *array;
int count;
int capacity;
void expandCapacity() {
Job *oldArray = array;
capacity *= 2;
array = new Job[capacity];
for (int i = 0; i < count; i++) {
array[i] = oldArray[i];
}
delete[] oldArray;
}
};
#endif
pq.cpp
#include <iostream>
#include <cstring>
using namespace std;
//#include "job.h"
#include "pq.h"
Job::Job() // Constructor
{
priority= 0;
taskName = "There are no items in the list.";
}
int Job::getPriority(){ // returns the prority of the job
return priority;
}
string Job::getTaskName(){ // returns the name of the job
return taskName;
}
void Job::setPriority(int val){ // sets the priority of a newly created job
priority = val;
}
void Job::setTaskName(string tname){ // sets the name of a new job
taskName = tname;
}
PriorityQueue::PriorityQueue() // constructor
{
count = 0;
capacity = INITIAL_CAPACITY - 1;
array = new Job[INITIAL_CAPACITY];
}
PriorityQueue::~PriorityQueue() { // destructor
delete [] array;
}
int PriorityQueue::size() { // returns the number of jobs in the queue
return count;
}
bool PriorityQueue::isEmpty() { // returns true if queue is empty
if (count != 0){
return false;
}else{
return true;
}
}
void PriorityQueue::clear() { // clears queue of all jobs
count = 0;
// need to make it remove and delete the items
}
void PriorityQueue::enqueue(string value, int priority) {
// tests size to see if Queue is a max capacity
if(count == capacity){
expandCapacity();
cout << "\tList was full and has been expanded\n";
}
array[++count].setPriority(priority);
array[count].setTaskName(value);
// upheap operations
Job v = array[count];
int tempcount = count;
while (array[tempcount/2].getPriority() >= v.getPriority()){
array[tempcount] = array[tempcount/2];
tempcount = tempcount/2;
array[tempcount] = v;
}
}
string PriorityQueue::dequeue() {
// removes the job with the highest priority from the queue and returns the name
if(this->isEmpty()){ // make sure the queue isnt empty
string empty = "The queue is empty";
return empty;
}else{
Job remove = array[1];
array[1] = array[count--];
int j;
Job v;
int k = 1;
v = array[k];
while(k <= count/2){
cout << "dequeuewhile"; // test
j = k + k;
if(j < count && array[j].getPriority() > array[j+1].getPriority()){
j++;
cout << "dequeueloop if1"; // test
}
if(v.getPriority() <= array[j].getPriority()){
cout << "dequeueloop if2"; //test
break;
}
array[k] = array[j];
k = j;
}
array[k] = v;
return remove.getTaskName(); // returns the name of the removed job
}
}
string PriorityQueue::peek() { // returns the name of the highest priority job without removing it from the queue
if(count == 0){
return array[0].getTaskName();
}
return array[1].getTaskName();
}
int PriorityQueue::peekPriority() { // returns the priority from the highest priority job without removing it from the queue
if(count == 0){
cout << "\tThere are no items in the list.\n";
return array[0].getPriority();
}
return array[1].getPriority();
}
I think that when you do ++count, the next use of count will be out of bounds for the array.
array[++count].setPriority(priority);
// SEGMENTATION FAULT HERE
array[count].setTaskName(value);
If the capacity of the array is 5, and count was 4, then you just incremented count to 5, and tried to access element 5, which is out-of-bounds.
array = new Job[capacity];
for (int i = 0; i < count; i++) {
array[i] = oldArray[i];
}
Lets assume capacity is 10, so you've got an array of 10 elements, ranging from elements 0 to 9.
counttells us how many elements are being used.
If count happens to be 9, then when you increment count by one, it is now 10. Then, when line come you marked as producing segment fault comes, you're trying to access element 10, in our example. There is no element 10in an array of length 10, so you're out of bounds.
array[++count].setPriority(priority); // array[10], but last element is 9!
// SEGMENTATION FAULT HERE
array[count].setTaskName(value); // array[10], but last element is 9!
And, of course, everything after that part causes the same issue, as you keep using array[count].
Your original code did exactly as the previous answer given by #antiHUMAN.
The problem you're having is mixing or erroneously using 0-based and 1-based concepts.
Your first mistake is to make capacity a 0-based number. The capacity should denote the maximum number of items in an array, thus you should not be subtracting 1 from it. If the array can hold 5 items, then capacity should be 5, not 4.
PriorityQueue::PriorityQueue() // constructor
{
count = 0;
capacity = INITIAL_CAPACITY; // this remains 1-based.
array = new Job[INITIAL_CAPACITY];
}
or using the initializer-list:
PriorityQueue::PriorityQueue() : count(0),
capacity(INITIAL_CAPACITY),
array(new Job[INITIAL_CAPACITY]) {}
The 0-based number in your situation should be count, not capacity. Given that, since count is 0-based, and capacity is 1-based, your test in enqueue needs to be changed:
if(count + 1 == capacity){
expandCapacity();
cout << "\tList was full and has been expanded\n";
}
Note that 1 is added to count to account for the fact that count is 0-based and capacity is 1 based.
So I thought I understood how to implement an array of pointers but my compiler says otherwise =(. Any help would be appreciated, I feel like I'm close but am missing something crucial.
1.) I have a struct called node declared:.
struct node {
int num;
node *next;
}
2.) I've declared a pointer to an array of pointers like so:
node **arrayOfPointers;
3.) I've then dynamically created the array of pointers by doing this:
arrayOfPointers = new node*[arraySize];
My understanding is at this point, arrayOfPointers is now pointing to an array of x node type, with x being = to arraySize.
4.) But when I want to access the fifth element in arrayOfPointers to check if its next pointer is null, I'm getting a segmentation fault error. Using this:
if (arrayOfPointers[5]->next == NULL)
{
cout << "I'm null" << endl;
}
Does anyone know why this is happening? I was able to assign a value to num by doing: arrayOfPointers[5]->num = 77;
But I'm confused as to why checking the pointer in the struct is causing an error. Also, while we're at it, what would be the proper protoype for passing in arrayOfPointers into a function? Is it still (node **arrayOfPointers) or is it some other thing like (node * &arrayOfPointers)?
Thanks in advance for any tips or pointers (haha) you may have!
Full code (Updated):
/*
* Functions related to separate chain hashing
*/
struct chainNode
{
int value;
chainNode *next;
};
chainNode* CreateNewChainNode (int keyValue)
{
chainNode *newNode;
newNode = new (nothrow) chainNode;
newNode->value = keyValue;
newNode->next = NULL;
return newNode;
}
void InitDynamicArrayList (int tableSize, chainNode **chainListArray)
{
// create dynamic array of pointers
chainListArray = new (nothrow) chainNode*[tableSize];
// allocate each pointer in array
for (int i=0; i < tableSize; i++)
{
chainListArray[i]= CreateNewChainNode(0);
}
return;
}
bool SeparateChainInsert (int keyValue, int hashAddress, chainNode **chainListArray)
{
bool isInserted = false;
chainNode *newNode;
newNode = CreateNewChainNode(keyValue); // create new node
// if memory allocation did not fail, insert new node into hash table
if (newNode != NULL)
{
//if array cell at hash address is empty
if (chainListArray[hashAddress]->next == NULL)
{
// insert new node to front of list, keeping next pointer still set to NULL
chainListArray[hashAddress]->next = newNode;
}
else //else cell is pointing to a list of nodes already
{
// new node's next pointer will point to former front of linked list
newNode->next = chainListArray[hashAddress]->next;
// insert new node to front of list
chainListArray[hashAddress]->next = newNode;
}
isInserted = true;
cout << keyValue << " inserted into chainListArray at index " << hashAddress << endl;
}
return isInserted;
}
/*
* Functions to fill array with random numbers for hashing
*/
void FillNumArray (int randomArray[])
{
int i = 0; // counter for for loop
int randomNum = 0; // randomly generated number
for (i = 0; i < ARRAY_SIZE; i++) // do this for entire array
{
randomNum = GenerateRandomNum(); // get a random number
while(!IsUniqueNum(randomNum, randomArray)) // loops until random number is unique
{
randomNum = GenerateRandomNum();
}
randomArray[i] = randomNum; // insert random number into array
}
return;
}
int GenerateRandomNum ()
{
int num = 0; // randomly generated number
// generate random number between start and end ranges
num = (rand() % END_RANGE) + START_RANGE;
return num;
}
bool IsUniqueNum (int num, int randomArray[])
{
bool isUnique = true; // indicates if number is unique and NOT in array
int index = 0; // array index
//loop until end of array or a zero is found
//(since array elements were initialized to zero)
while ((index < ARRAY_SIZE) && (!randomArray[index] == 0))
{
// if a value in the array matches the num passed in, num is not unique
if (randomArray[index] == num)
{
isUnique = false;
}
index++; // increment index counter
} // end while
return isUnique;
}
/*
*main
*/
int main (int argc, char* argv[])
{
int randomNums[ARRAY_SIZE] = {0}; // initialize array elements to 0
int hashTableSize = 0; // size of hash table to use
chainNode **chainListArray;
bool chainEntry = true; //testing chain hashing
//initialize random seed
srand((unsigned)time(NULL));
FillNumArray(randomNums); // fill randomNums array with random numbers
//test print array
for(int i = 0; i < ARRAY_SIZE; i++)
{
cout << randomNums[i] << endl;
}
//test chain hashing insert
hashTableSize = 19;
int hashAddress = 0;
InitDynamicArrayList(hashTableSize, chainListArray);
//try to hash into hash table
for (int i = 0; i < ARRAY_SIZE; i++)
{
hashAddress = randomNums[i] % hashTableSize;
chainEntry = SeparateChainInsert(randomNums[i], hashAddress, chainListArray);
}
system("pause");
return 0;
}
arrayOfPointers = new node*[arraySize];
That returns a bunch of unallocated pointers. Your top level array is fine, but its elements are still uninitialized pointers, so when you do this:
->next
You invoke undefined behavior. You're dereferencing an uninitialized pointer.
You allocated the array properly, now you need to allocate each pointer, i.e.,
for(int i = 0; i < arraySize; ++i) {
arrayOfPointers[i] = new node;
}
As an aside, I realize that you're learning, but you should realize that you're essentially writing C here. In C++ you have a myriad of wonderful data structures that will handle memory allocation (and, more importantly, deallocation) for you.
Your code is good, but it's about how you declared your InitDynamicArrayList. One way is to use ***chainListArray, or the more C++-like syntax to use references like this:
void InitDynamicArrayList (int tableSize, chainNode **&chainListArray)
Hey all! Having a little trouble with my stack. Im trying to print each element that I've pushed onto the stack.
Starting with the stack ctor we know that we have a fixed size for the array. So I allocate the items struct object to hold just that much space:
stack::stack(int capacity)
{
items = new item[capacity];
if ( items == NULL ) {
throw "Cannot Allocoate Sufficient Memmory";
exit(1);
}
maxSize = capacity;
top = -1;
}
Yes, items is a struct type of the object "item". Have a look:
class stack
{
stack(int capacity);
~stack(void);
...
private:
int maxSize; // is for the item stack
int top; // is the top of the stack
struct item {
int n;
};
item *items;
public:
friend ostream& operator<<(ostream& out, stack& q)
...
First and formost we want to add to the stack by pushing each incoming element into the array FILO:
bool stack::pushFront( const int n )
{
if ( top >= maxSize-1 )
{
throw "Stack Full On Push";
return false;
}
else
{
++top;
items[top].n = n;
}
return true;
}
// just a textbook example here:
stack::~stack(void)
{
delete [] items;
items = NULL;
maxSize = 0;
top = -1;
}
Yes the real issue for me is the items[++top].n = n; statement. I've been trying to find out how I can drag (+) the items array out to see ALL of the array elements after I push onto the stack.
Im wondering why I cant drag that items[++top].n = n statement out when im debugging. All that comes up is the value that is passed as an 'n' paramater. Do I need to use a stack object type array to store the values into?
When I overload the << operator and try to print the elements I get an insanely large negative number:
ostream& operator<<(ostream& out, stack& q)
{
if ( q.top <= 0 ) // bad check for empty or full node
out << endl << "stack: empty" << endl << endl;
else
for ( int x = 0; x < q.maxSize; x++ )
{
out << q.items[x].n; // try to print elements
}
return out;
}
I'm way off and I need some guidence if anyone has the time!
In the overloaded << operator in the for loop you are iterating maxsize times. But you might not have pushed maxsize elements into the stack. You should iterate top times. Also, write a default constructor for item structure and initialize all the variblaes so that you do not get garbage values when you try to print them.
When printing the stack, you should only go up to top, not up to maxSize.