Reputation: 648
I have a homework assignment that I've nearly completed.
As inefficient as it is be I was just wanting to know how I can prevent a crash when my program ends.
quack::quack(int capacity) : backPtr( NULL ), frontPtr( NULL )
{
items = new item[capacity];
backPtr = new item;
frontPtr = new item;
midPtr = new item;
current = new item;
maxSize = capacity;
back = maxSize-1;
count = 0;
top = -1;
}
quack::~quack(void)
{
delete frontPtr;
delete backPtr;
delete current;
delete midPtr;
delete [] items; //Heap Corruption Debug Error at the end of program.
items = NULL;
maxSize = 0;
back = 0;
}
bool quack::pushFront(const int n)
{
int i = 0;
if ( count == maxSize ) // Then we cant add to it n e more.
{
throw runtime_error( "Stack is Full" );// Full Stack
return false;
}
backPtr->n = items[back-1].n;
while ( i < count ) // Loop less than however many we counted.
{
if ( i == top+1 )
{
current->n = items[top+1].n;
items[top+1].n = backPtr->n;
}
midPtr->n = items[++i].n;
items[i].n = current->n;
if ( i != back-1 )
{
current->n = items[++i].n;
items[i].n = midPtr->n;
}
}
++count;
items[top+1].n = n;
return true;
}
bool quack::pushBack(const int n)
{
items[count].n = n;
count++;
return true;
}
bool quack::popFront(int& n)
{
n = items[top+1].n;
for ( int i = 0; i < count; i++ )
{
items[i] = items[i+1];
}
count--; // Remove top element.
return true;
}
bool quack::popBack(int& n)
{
n = items[--count].n;
return true;
}
void quack::rotate(int r)
{
int i = 0;
while ( r > 0 ) // rotate postively.
{
frontPtr->n = items[top+1].n;
for ( int i = 0; i < back; i++ )
{
items[i] = items[i+1];
}
items[back-1].n = frontPtr->n;
r--;
}
while ( r < 0 ) // rotate negatively.
{
if ( i == top+1 )
{
backPtr->n = items[back-1].n;
current->n = items[top+1].n;
items[top+1].n = backPtr->n;
}
midPtr->n = items[++i].n;
items[i].n = current->n;
if ( i == back-1 )
{
items[back-1].n = current->n;
i = 0;
r++; continue;
}
else
{
current->n = items[++i].n;
items[i].n = midPtr->n;
if ( i == back-1 )
{
i = 0;
r++; continue;
}
}
}
}
void quack::reverse(void)
{
int j = 0; // Variable declaration/initialization.
frontPtr->n = items[top+1].n;
backPtr->n = items[back-1].n;
for ( int i = 0; i < count / 2; i++ )
{
items[j].n = items[i].n;
items[i].n = items[ count - i-1 ].n;
items[ count - i-1 ].n = items->n;
}
items[top+1].n = backPtr->n;
items[back-1].n = frontPtr->n;
}
int quack::itemCount(void)
{
return count;
}
ostream& operator<<(ostream& out, quack& q)
{
if ( q.count == 0 ) // No elements have been counted.
out << endl << "quack: empty" << endl;
else
{
out << endl << "quack: ";
for ( int i = 0; i < q.count; i++ )
{
if ( i < q.count-1 )
out << q.items[i].n << ", ";
else out << q.items[i].n;
}
out << endl << endl;
}
return out;
}
and the header file:
#include <ostream>
using namespace std;
class quack
{
public:
quack(int capacity);
~quack(void);
bool pushFront(const int n); // Push an item onto the front.
bool pushBack(const int n); // Push an item onto the back.
bool popFront(int& n); // Pop an item off the front.
bool popBack(int& n); // Pop an item off the back.
void rotate(int r); // "rotate" the stored items (see note below).
void reverse(void); // Reverse the order of the stored items.
int itemCount(void); // Return the current number of stored items.
private:
int maxSize; // is for the size of the item stack
int back; // is for the back or "bottom" of the stack
int count; // to count the items added to the stack
int top;
struct item // Definition of each item stored by the quack.
{
int n;
};
item *items; // Pointer to storage for the circular array.
item *backPtr;
item *frontPtr;
item *midPtr;
item *current;
public:
friend ostream& operator<<(ostream& out, quack& q);
};
Upvotes: 0
Views: 1092
Reputation: 36082
Pointers is one of those things that takes some time to get used to.
When you allocate
items = new item[capacity]
you effectively have a pointer 'items' pointing to the first element of an array of 'item' allocated on the heap:
items -> {item}{item}{item}..{item} // capacity
Your other pointers should then point to the same array as well and shouldn't later be used to delete what they point to, instead you just delete 'items':
delete [] items
Arrays and pointers are interchangeable.
items + 1 is the same thing as &items[1] *(items + 1) is the same thing as items[1].
This makes 'current' point to second item in array:
current = items + 1
This doesn't work because 'current' is a pointer and items[1] is an item object in the array, not an address:
current = items[1]
This works however:
current = &items[1]
Upvotes: 2
Reputation: 2301
Your main problem is to understand that backptr, frontptr, midptr and current have a different purpose than items. Technically, they all point to some memory location, but semantically items is kind of an anchor for your data container (an array), while the other pointers' purpose is managing that data (book keeping).
So items is the only pointer you should assign memory to you allocate. The other pointers should point into the array (list) items points at.
As a consequence you should set backptr, frontptr, midptr and current to NULL in the c'tor and d'tor and only use new and delete with items.
Btw, I don't know whether your task allows it, but using a linked list instead of an array might make your life easier regarding managing the list (as might using type int for backptr, frontptr, midptr and current).
Upvotes: 1
Reputation: 116674
Two possible causes for the crash when deleting the items
array.
The class has no custom copy constructor or assignment operator. This is essential because the built-in ones will be no good. If you ever pass a quack
by value as a parameter, or return one from a function, or copy a quack
into a variable, you will have two quack
s pointing at the same items
array. When the second one destructs, it will try to delete
the array a second time, and as John Lennon observed in his famous song about deleting heap objects, "No, no no, not a second time".
You do a lot of writing to the items
array within your code. If you write past the end (or before the start) of the array, that will be detected when you delete it because at that point the heap implementation will find that you've trashed some management information that it needs to free the block of memory. It's certainly possible that you have a bug in that code, as most people do when they start trying to do this.
In C++ there is an easy way to detect this - don't use a raw array. Use a class that wraps an array and performs bounds checking on every access to it. You can get this with std::vector
and using the at
function instead of the []
operator (which is not bounds-checked).
Upvotes: 1
Reputation: 51157
I'm going to be editing this a few times as I read through the code, please forgive me. It appears you're implementing a double-ended queue (dequeue).
items = new item[capacity];
backPtr = new item;
frontPtr = new item;
midPtr = new item;
current = new item;
This doesn't make sense. Your front/back/mid/current pointers don't actually point to one of your items. You probably want to have frontPtr = items+0
and backPtr = items + capacity-1
(or vice versa). Not sure what a dequeue needs a midPtr or current for.
[Edit: It seems item is a struct item { int n }
and you're just copying n around. And you have a back index and top index... ]
delete frontPtr;
delete backPtr;
delete current;
delete midPtr;
delete [] items; //Heap Corruption Debug Error at the end of program.
Since front/back/etc. should point inside of items, you're double-freeing some of these items. That may be your heap corruption crash. [Edit: or not, considering the weird copying]
items = NULL;
maxSize = 0;
back = 0;
This seems rather silly (the object is about to be no more; who cares?)...
Ok, the normal way a simple dequeue would work is to have an array of elements:
items -> 1 2 3 4 5 6 7 8 9 …
front_ptr -----------/ /|\
back_ptr --------------------------------+
and then you'd have a pointer (frontPtr) which points to the first used spot in the array and another pointer (backPtr) that points to the last used spot. So, a pop_front would do something like this:
if (frontPtr <= backPtr) {
return *frontPtr++
} else {
// tried to pop from empty dequeue, handle error
}
That would return 3, and advance front_ptr to point to 4. pop_back would be similar (but with the test reversed, and with -- instead of ++).
Alternatively, instead of pointers, you could store the indices. But pick one, don't use both indices and pointers.
Upvotes: 5