I am working on an assignment that involves me writing a template class for a queue. It uses a dynamically allocated array. I am having trouble with the copying of the array. This is the prompt.
aQueue.setCapacity(newCapacity), that changes the capacity of aQueue to newCapacity. (This is much trickier than Stack::setCapacity(): if newCapacity is zero or < getSize(), setCapacity() should throw an exception; otherwise, it should allocate a new array with the new capacity, copy the values from the old array into the new array, and deallocate the old array.) To help you see all the things that can go wrong, you should make certain your method passes all the tests in this test method before working on the rest of the project.
Instance variables are:
unsigned mySize; // number of items I contain
unsigned myCapacity; // how many items I can store
unsigned myFirst; // index of oldest item (if any)
unsigned myLast; // index of next available spot for append (if any)
Item* myArray; // dynamic array of items
This is what I have so far:
template <class Item>
void ArrayQueue<Item>::setCapacity(unsigned newCapacity) {
if (newCapacity == 0 || newCapacity < mySize) {
throw QueueException("setCapacity()","newCapacity is too small");
} else {
Item * newArray = new Item[newCapacity];
unsigned y = myFirst;
for (unsigned x = 0; x < mySize; x++) {
newArray[y] = myArray[x];
y++;
}
delete [] myArray;
myArray = newArray;
myCapacity = newCapacity;
myLast = y;
}
}
These are the methods for getFirst() and getLast():
// Method returns first Item in ArrayQueue
template <class Item>
unsigned ArrayQueue<Item>::getFirst() const {
if (this->isEmpty()) {
throw EmptyQueueException("getFirst()");
} else {
return myArray[myFirst];
}
}
// Method returns last Item in ArrayQueue
template <class Item>
unsigned ArrayQueue<Item>::getLast() const {
if (this->isEmpty()) {
throw EmptyQueueException("getLast()");
} else {
return myArray[(myLast - 1 + myCapacity) % myCapacity];
}
}
I have been working on this for hours so any help would be greatly appreciated.
Credit goes to: Aconcagua
for (unsigned x = 0; x < mySize; x++) {
newArray[x] = myArray[(y % myCapacity)];
y++;
}
delete [] myArray;
myArray = newArray;
myCapacity = newCapacity;
myFirst = 0;
myLast = mySize;
By setting myFirst to 0 and myLast to mySize this properly allocates the indexes correctly. In addition to this when copying the array you most set newArray[x] to myArray[(y % myCapacity)].
Related
Inside the ArrayList I'm trying to delete all possible 0's that are appended as input, but for now it only deletes just one 0, no matter where it is located. But seems like I can't delete more than one zero at the time. How can I fix this?
void AList::elimZeros(){
int i;
int curr = 0;
for(i=0; i < listSize; i++) {
if ( (listArray[i] != 0 ) && (curr<listSize) ){
listArray[curr] = listArray[i];
curr++;
}
else if (listArray[i] == 0 )
{
listArray[curr] = listArray[i+1];
listSize--;
curr++;
}
}
}
This is the class for the ADT
class AList : public List {
private:
ListItemType* listArray; // Array holding list elements
static const int DEFAULT_SIZE = 10; // Default size
int maxSize; // Maximum size of list
int listSize; // Current # of list items
int curr; // Position of current element
// Duplicates the size of the array pointed to by listArray
// and update the value of maxSize.
void resize();
public:
// Constructors
// Create a new list object with maximum size "size"
AList(int size = DEFAULT_SIZE) : listSize(0), curr(0) {
maxSize = size;
listArray = new ListItemType[size]; // Create listArray
}
~AList(); // destructor to remove array
This is the input I'm testing with:
int main() {
AList L(10);
AList L2(20);
L.append(10);
expect(L.to_string()=="<|10>");
L.append(20);
expect(L.to_string()=="<|10,20>");
L.append(30);
L.append(0);
L.append(40);
L.append(0);
L.append(0);
expect(L.to_string()=="<|10,20,30,0,40>");
L.elimZeros();
expect(L.to_string()=="<|10,20,30,40>");
assertionReport();
}
It'd be helpful if you posted the class code for AList. Think you confused Java's ArrayList type, but assuming you're using vectors you can always just do:
for (int i = 0; i < listSize; i++) {
if(listArray[i] == 0) listArray.erase(i);
}
EDIT: Assuming this is the template of for the AList class, then there is simply a remove() function. In terms of your code, there are two issues.
You reference listSize in the for loop, then decrement it inside of the loop. Each iteration evaluates the value separately so you're reducing the number of total loop iterations and stopping early.
The other thing is if the entry is zero you shouldn't increment curr and set listArray[curr] = listArray[i+1]. This is basically assuming the next entry will not be a zero. So if it is, then you're copying the element and moving to the next. Your if statement can be cleaned up with:
if (listArray[i] == 0) {
listSize--;
} else {
listArray[curr] = listArray[i];
curr++;
}
I'm trying to write a function that pushes an item onto the end of my dynamically allocated array (not allowed to use vectors). Once it goes to the area to double the size of the list if the list was too small to store the next number, it all goes to hell and starts feeding me back random numbers from the computer. Can anyone see why it's not doubling like it's suuposed to?
int *contents_;
int *temp;
int size_ = 0;
int capacity_ = 1;
void pushBack(int item) /**appends the specified value to DynArray; if the contents array is full,
double the size of the contents array and then append the value **/
{
if (size_ == capacity_)
{
capacity_ = (2*capacity_);
temp = new int[capacity_];
for (int i = 0; i < size_; ++i)
{
temp[i] = contents_[i];
}
delete [] contents_;
contents_ = temp;
}
contents_[size_++] = item;
}
EDIT ** I forgot to mention. This is a function out of a class. This is in the header and in main :
main()
{
DynArray myArray;
myArray.pushBack(2);
myArray.pushBack(3);
myArray.printArray();
return 0;
}
If this is your initial setup:
int *contents_; // Junk
int size_ = 0;
int capacity_ = 1;
Then your code is most likely performing a memory access violation upon the first time it does:
if (size_ == capacity_)
{
// Not entering here, contents_ remains junk
}
contents_[size_++] = item;
As barak implied, the contents_ pointer needs to be initialized. If not, c++ will point it to something you probably don't want it to.
Here's my code:
template<class T> class Test
{
public:
int Size = 0;
int Length = 0;
T* Items;
Test() {}
~Test()
{
delete [] Items;
}
void Append(const T& newItem)
{
if (Size + 1 >= Length)
{
Length += 250;
T* old = Items;
Items = new T[Length + 250];
for (int i = 0; i < Size; i++)
Items[i] = old[i];
delete [] old;
}
Items[Size] = newItem;
Size++;
}
};
Test<int> test;
for (int i = 0; i < 500000; i++)
test.Append(i);
I'm populating the dynamic array with 500000 integers which must take just 1-2Mb but it takes about 30Mb. There's no problem if i set the initial size to 500000(i.e. no resizing occurring). The grow value(250) seems to affect the memory somehow, if it's larger(for example 1000) then the memory usage is pretty low. What's wrong?
Typically, when you are reallocating an array, you do not want to modify the actual array until the very last second (to maintain exception safety):
T* temp = new T[new_size];
// assume count is the previous size and count < new_size
std::copy(Items, Items + count, temp);
std::swap(temp, Items);
delete [] temp;
Aside from that, there is nothing visible in your code that would cause a memory leak.
The extra size can possibly be due to other optimizations (being turned off) and/or debugging symbols being turned on. What compiler options are you using (and what compiler)? It should be noted that extra size is not necessarily an indication of a memory leak. Have you run this in a debugger or memory profiler which found a leak?
It should also be noted that std::vector does all of this for you.
Looking at your code, you're going to segfault more so than leak memory due to the fact that calling delete or delete[] on a non-NULL, but previously deallocated, pointer is a Bad Thing. Also, I don't believe this is your real code, because what you posted won't compile.
When you delete a pointer, always set it to NULL afterwards. It's good practice to initialize to NULL as well. Let's fix up your code to make sure we don't call delete on previously deallocated pointers. Also, let's initialize our pointer to NULL.
Your misuse of memory probably stems from the following lines of code:
Length += 250;
T* old = Items;
Items = new T[Length + 250];
Notice that you increment Length by 250, but then allocate Length+250 more elements? Let's fix that, too.
template<class T>
class Test
{
public:
int Size;
int Length;
T* Items;
Test() : Size(0), Length(0), Items(NULL){}
~Test() {
if (Items != NULL)
delete [] Items;
}
void Append(const T& newItem)
{
if (Size + 1 >= Length)
{
Length += 250;
T* old = Items;
Items = new T[Length];
for (int i = 0; i < Size; i++)
Items[i] = old[i];
delete [] old;
old = NULL;
}
Items[Size] = newItem;
Size++;
}
};
int main(){
Test<int> test;
for (int i = 0; i < 500000; i++)
test.Append(i);
}
Trying to make my own Map struct to store my own-created 'Strings,' and after 8 hours or so finally got it down to only a few compiler errors (six of them). I've spent the last hour and forty minutes searching the web for answers, only to find people forgot default constructors, and tried mixing things up in my own program. Since I'm not really sure where the problem is in advance, I apologize for posting all this code...I put what I thought were the most relevant files first; I think only the first 3 are necessary. The error is
"SubdomainPart' : No appropriate default constructor available" for lines 12 and 20 of the Map.h file.
Map.h
// Map.h - Map template class declaration
// Written by -----
#pragma once
template<typename KEY_TYPE, typename VALUE_TYPE>
struct Map
{
public:
// Default / initial constructor hybrid
Map(int initialCapacity = 10)
{
Size = 0;
Capacity = initialCapacity;
Key;
MappedValue;
//Allocate the C-Array elements using HEAP
Data = new VALUE_TYPE[Capacity];
}
struct iterator
{
KEY_TYPE * current;
KEY_TYPE * prev;
KEY_TYPE * next;
iterator operator ++ ()
{
iterator it = this;
iterator itNext = it.next;
it.next = itNext.next; // pushes iterator forward.
it.prev = it.current;
it.current = it.next;
}
iterator operator -- ()
{
iterator it = this;
iterator itPrev = it.prev;
it.prev = itPrev.prev; // pushes iterator backward.
it.next = it.current;
it.current = it.prev;
}
};
Map(const Map& copyFrom)
{
// Necessary to prevent the delete[] Data; statement in the assignment operator from
// freezing because Data has some garbage address in it.
Data = NULL;
*this = copyFrom; //'this' points to the current instance of the object. (in this case, 'Map')
}
// Destructor: MUST HAVE because we allocate memory
~Map()
{
delete[] Data;
}
Map& operator = (const Map& copyFrom)
{
// 0) delete the old one!
delete[] Data;
// 1) copy Size and Capacity
Size = copyFrom.Size;
Capacity = copyFrom.Capacity;
// 2) Allocate Memory
Map* Data = new Map[Capacity];
// 3) Copy the Map Elements
for(int i = 0; i<Size; i++)
Data[i] = copyFrom.Data[i];
return *this;
}
// Index Operator
VALUE_TYPE& operator[] (KEY_TYPE key) const
{
return Data[key];
}
// Accessor functions: read-only access to Size and Capacity
int GetSize() const //const does not modify ANY data members of the class (size, capacity, or data)
{
return Size;
}
int GetCapacity() const
{
return Capacity;
}
void PushBack(const VALUE_TYPE& newElement) //adds value to end of Map as default
{
if(Size >= Capacity)
increaseCapacity(2 * Capacity);
Data[Size] = newElement;
Size++; // increases size of the array so it can be used later.
}
// Overloaded Add function, inserts a value at specified index, calls in "Insert" to do so.
void Add(const VALUE_TYPE& newElement, int index)
{
if( (index<0) || (index > Size))
{
throw ("Index to insert is out of range");
}
//Make sure there's space!
if (Size >= Capacity)
increaseCapacity(2*Capacity); //increase size of array if too small!
Insert(index, newElement);
}
void Remove(int index) // index = index to be removed.
{
// Make sure it's inside the bounds
if( (index<0) || (index > Size))
{
throw ("Index to Remove is out of range.");
}
// it's going to remove the unneeded space by having its capacity one above the Size.
Map* new_Data = new Map[Size];
//Copy data onto new pointer section.
for(int x = 0; x<Size; x++)
new_Data[x] = Data[x];
delete[] Data; //deallocates old memory and uneeded capacity slots.
for(int x = index; x < (Size - 1); x++) //removes the value at index 'index.' Now Data has a capacity of the amount of slots used and one more for a NULL value.
new_Data[x] = new_Data[x+1];
Data = new_Data;
Data[Size-1] = NULL;
Size--;
}
void increaseCapacity(int new_capacity)
{
if(new_capacity>Capacity)
{
if(new_capacity> 2* Capacity)
Capacity = new_capacity;
else
Capacity *= 2;
//create Map with a new capacity!
Map* new_Map = new Map[Capacity];
for(int x = 0; x<Size; x++)
{
new_Map[x] = Data[x];
}
//clear out old memory
delete[] Data;
//set data pointer to the new Map
Data = new_Map;
}
}
KEY_TYPE * Key; // Used to identify mapped values.
VALUE_TYPE MappedValue; // The value actually contained.
private:
int Size; // The count of actual C-Array elements used
int Capacity; // The count of C-array elements allocated
// The encapsulated C-array
VALUE_TYPE * Data; // pointer of type 'DATA_TYPE' called data (will be name of our array).
void Insert(const int index, const VALUE_TYPE& insertValue)
{
if( (index<0) || (index > Size))
{
throw out_of_range ("Index to insert is out of range");
}
//Time to shuffle the array down!
for(int x = Size; x>index; x--)
{
Data[x] = Data[x-1];
}
//Insert the new item at index 'Index!'
Data[index] = insertValue;
Size++;
}
};
SubdomainPart.h
// SubdomainPart.h - SubdomainPart validation class declaration
// Written by -------
#pragma once
#include "String.h"
using namespace std;
class SubdomainPart
{
public:
// Takes the address and stores into the Address data member
SubdomainPart(const String& address);
// Returns true when the Address is valid or false otherwise
virtual bool IsValid();
private:
String Address;
};
SubdomainPart.cpp
// SubdomainPart.cpp - Subdomain validation class implementation
// Written by ---------
#pragma once
#include "SubdomainPart.h"
// Takes the address and stores into the Address data member
SubdomainPart::SubdomainPart(const String& address)
{
Address = address;
}
// Returns true when the Address is valid or false otherwise
bool SubdomainPart::IsValid()
{
int currentDotIndex = 0;
int nextDotIndex = 0;
int found = 0; // first index of a found invalid character
int hyphenIndex = 0; // used to check hyphen rule
// 1. Check the size, 255 total characters
if(Address.GetLength() < 1 || Address.GetLength() > 255)
return false;
// Checks for valid amount of 1-63 characters between dots
currentDotIndex = Address.FindFirstOf('.');
if(currentDotIndex == 0 || currentDotIndex == Address.GetLength()-1)
return false;
else if(currentDotIndex!=(-1))
nextDotIndex = Address.Find('.', currentDotIndex+1);
else
nextDotIndex = (-1); // if no '.' is found, ensures the following loop doesn't run.
while(nextDotIndex!=(-1))
{
if((nextDotIndex-currentDotIndex) == 1 || (nextDotIndex-currentDotIndex) > 63)
return false;
currentDotIndex = nextDotIndex;
nextDotIndex = Address.Find('.', currentDotIndex+1);
}
// 2. Check for valid characters
found = Address.FindFirstNotOf("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890-.");
if(found!=(-1)) // if a character not listed above is found.
return false;
// 3. Check for dash rule
// Making sure hyphens aren't located at the first or last index of a subdomain.
hyphenIndex = Address.FindFirstOf('-');
if(hyphenIndex == 0)
return false;
hyphenIndex = Address.FindLastOf('-');
if(hyphenIndex == Address.GetLength()-1)
return false;
// Makes sure two hyphens aren't in a row.
for(int x = 1; x<Address.GetLength(); x++)
if(Address[x] == '-' && Address[x] == Address[x-1])
return false;
return true;
}
I don't see a default constructor in this class:
class SubdomainPart
{
public:
// Takes the address and stores into the Address data member
SubdomainPart(const String& address);
// Returns true when the Address is valid or false otherwise
virtual bool IsValid();
private:
String Address;
};
Keep in mind that this map constructor is default-constructing every member rather than initializing them:
Map(int initialCapacity = 10)
{
Size = 0;
Capacity = initialCapacity;
Key;
MappedValue;
//Allocate the C-Array elements using HEAP
Data = new VALUE_TYPE[Capacity];
}
You don't have a default constructor for SubdomainPart you have only provided a copy constructor. A default constructor takes no argument.
The compiler is complaining that SubdomainPart doesn't have a default constructor, and indeed it doesn't. It's required because your Map contains an object of type VALUE_TYPE:
VALUE_TYPE MappedValue;
Also, your Map constructor contains very weird code. I assume you actually wanted to use an initialiser list:
Map(int initialCapacity = 10)
: Key()
, MappedValue()
, Size(0)
, Capacity(initialCapacity)
, Data(new VALUE_TYPE[Capacity])
{}
The problem is with Data = new VALUE_TYPE[Capacity]; part.
The compiler generates code to allocate the array and instantiate each element by calling the parameterless constructor for VALUE_TYPE. As SubdomainPart doesn't have one (since you have defined a custom one), the compiler throws an error.
The reason that compiler reports error in map.h is that it is exactly the place where the constructor is called from. It is not used in SubdomainPart code, it is just defined there.
I am trying to insert an int into an array that is in a class object, and I cannot figure out what I am doing wrong. The current state of my code never inserts the int into the array.
Basically what I am trying to do is when i call insert(int) it will check to to see if there is any room left in the array, and if there is it will add it, otherwise it would reallocate with 8 more spaces in the array.
here is some relevant class info
private:
unsigned Cap; // Current capacity of the set
unsigned Num; // Current count of items in the set
int * Pool; // Pointer to array holding the items
public:
// Return information about the set
//
bool is_empty() const { return Num == 0; }
unsigned size() const { return Num; }
unsigned capacity() const { return Cap; }
// Initialize the set to empty
//
Set()
{
Cap = Num = 0;
Pool = NULL;
}
here is the code i am working on
bool Set::insert(int X)
{
bool Flag = false;
if (Num == Cap)
{
//reallocate
const unsigned Inc = 8;
int * Temp = new int[Cap+Inc];
for (unsigned J=0;J<Num;J++)
{
Temp[J] = Pool[J];
}
delete [] Pool;
Pool = Temp;
Cap = Cap+Inc;
}
if(Num < Cap)
{
Pool[Num+1] = X;
Flag = true;
}
return Flag;
}
Your insert function never updates Num. Try Pool[Num++] = X; or something like that.
You probably want to increment the number of element but only after copying the new element in: the first element should have index 0. Basically, your insert() function should look something like this:
bool Set::insert(int X)
{
if (Num == Cap)
{
const unsigned Inc(std::max(8, 2 * Cap));
std::unique_ptr<int[]> Temp(new int[Cap+Inc]);
std::copy(Pool.get(), Pool.get() + Num, Temp.get());
Pool.swap(Temp);
Cap += Inc;
}
Pool[Num] = X;
++Num;
return true;
}
Of course, this assumes that Pool is reasonably declared as std::unique_ptr<int[]> (or something with similar functionality which is easy to write if necessary). The reason to use std::unique_ptr<int[]> rather than raw pointers is that they automatically clean up resources when they are destroyed. Copying a sequence of ints won't throw an exception but if int get's replaced by a std::string or a template parameters there is potential to throw exceptions.