So I'm trying to make a template class for a heap. I'm trying not to use any of the STL classes (professor's rules and whatnot) so rather than use a vector I use a dynamically allocated array. However, when I run the program and input enough data into the heap for it to trigger the resizing function, it springs a bunch of memory errors. Some gdb work narrowed it down to this function:
template <class T>
T* Heap<T>::resize()
{
T* temp; //temp variable for storage
heap_capacity *= 2; //double capacity
temp = new T[heap_capacity]; //create new enlarged array
for (int i = 0; i < heap_size; ++i)
{
temp[i] = heap_arr[i]; //copy elements from previous array to current
}
delete [] heap_arr; //delete old array
return temp; //return new array
}
Pretty standard stuff, tbh. It requires whatever T is to have an assignment operator, but I was testing it using plain old integers. This isn't the first time I've written this code, but it is the first time I've written it for a template. Does the problem lie in this function, or is it somewhere else?
Edit: I played around with the code a little bit more in gdb. Turns out the program errors out right after I allocate memory for temp. This is strange, because I do the same thing for the original heap_array in the constructor. I'll be poking around a bit more, but is my syntax wrong for the "new" statement? heap_capacity is valid, btw, so that's not the problem...
It looks like you need to set heap_arr to point to the new expanded memory. It would certainly make sense for the resize method to take care of this:
delete [] heap_arr;
heap_arr = temp;
return heap_arr;
You also need to be sure that the heap_size is less than heap_capacity.
Well, I've figured out why I was getting the error. Turns out my check to see if the size of the heap was equal to the capacity forgot a crucial part: the extra 0 at the top of the array. The heap is 1-based (as opposed to 0-based) so the fix was simple: change the heap_size == heap_capacity statement to heap_size == heap_capacity-1... an important distinction. Thank you all for your help.
Related
I would like to create a vector of pointers to struct
vector<myStruct*> vec
For elements in the vector, not all of them contain data. Some of them may point to NULL.
So, should I create space by new in each of the element first
for(int i = 0; vec.size() ;i++){
if (thisSpaceIsValid(i))
vec.at(i) = new myStruct;
else
vect.at(i) = NULL;
}
The problem comes:
-If I use new for each element, it would be very slow. How can I speed it up a bit? Is there a way the create all the spaces that I need , that automatically access the pointer of such space to the vector(vec here)?
-If later I use delete to free the memory, would the problem of speed still bother me?
If I use "new" for each element, it would be very slow. How can I speed it up a bit? Is there a way the create all the spaces that I need , that automatically access the pointer of such space to the vector("vec" here)?
You can do that.
Let's say the size of your vector is M and you only need N of those elements to have pointers to objects and other elements are null pointers. You can use:
myStruct* objects = new myStruct[N];
and then, use:
for(int i = 0, j = 0; vec.size(); i++)
{
if (thisSpaceIsValid(i))
{
if ( j == N )
{
// Error. Do something.
}
else
{
vec[i] = objects+j;
++j;
}
}
else
{
vect[i] = NULL;
}
}
You have to now make sure that you are able to keep track of the value of objeccts so you can safely deallocate the memory by using
delete [] objects;
PS
There might be a better and more elegant solution to your problem. It will be worth your while to spend a bit more time thinking over that.
EDIT:
After reading the question again, it seems I misunderstood the question. So here is an edited answer.
If you only need to execute the code during some kind of initialization phase, you can create all the instances of myStruct in an array and then just point to those from the vector as already proposed by R Sahu. Note that the solution requires you to create and delete all instances at the same time.
However, if you execute this code several times and/or don't know exactly how many myStruct instances you will need, you could overwrite new and delete for the struct and handle memory allocation yourself.
See Callling object constructor/destructor with a custom allocator for an example of this. See the answer by Jerry Coffin.
BTW - you don't need vec.at(i) as you are iterating from 0 to size. vec[i] is okay and should perform a better.
OLD ANSWER:
You can do
vector<myStruct*> vec(10000, nullptr);
to generate a vector with for instance 10000 elements all initialized to nullptr
After that you can fill the relevant elements with pointer to the struct.
For delete just
for (auto e : vec) delete e;
cause it is safe to do deleteon a nullptr
If you need a vector of pointers, and would like to avoid calling new, then firstly create a container of structs themselves, then assign pointers to the elements into your vec. Be careful with choosing the container of structs. If you use vector of structs, make sure to reserve all elements in advance, otherwise its elements may move to a different memory location when vector grows. Deque on the other hand guarantees its elements don't move.
Multiple small new and delete calls should be avoided if possible in c++ when performance matters a lot.
The more I think about it, the less I like #RSahu's solution. In particular, I feel memory management in this scenario would be a nightmare. Instead I suggest using a vector of unique_ptr's owning memory allocated via custom alloctor. I believe, sequential allocator would do.
So I have to dynamically grow a static array in this struct I've made (I COULD use vectors but it specially asks me to use a static array and copy the elements so please don't ask me to use vectors).
Problem is I get segmentation faults and I don't know why. When I create a new instance of my object it sets an array with a length (currently 5 to test). I then run this code when resizing the array but it isn't working.
if (used == (length-1)) {
length *= 2;
T* newArr = new T[length];
for (int i = 0; i < used; i++) {
newArr[i] = items[i];
cout << "Copied" << endl;
}
items = newArr;
}
So it seems to copy to addresses over to the new array OK and seems to only copy the appropriate number of items. Used determines how many items have been added whereas length holds the size of the entire array. Such that when I create a new array the length should be double how many are used. When I try and assign items to newArr I get a segmentation fault so I'm wondering if that code is correct or if it's crashing there. I;ve tried debugging it line by line but can't tell if it's breaking there or not.
Q: "I COULD use vectors but it specially asks me to use a static array and copy the elements so please don't ask me to use vectors..."
A: Then the question is fundamentally flawed ;) You SHOULD use vectors ;)
If you really want to amuse your instructor, I'd encourage you to usemalloc(), realloc() and free() instead of "new" and "delete" ;)
Meanwhile - you're probably getting a segmentation fault by overwriting "newArr[]". "newArr[]" might be too small if "used" is a lot bigger than length.
Just a guess - using a debugger would clearly show you exactly what's happening.
Q: What compiler and debugger are you using?
Do a
delete[] items;
before the reassignment
Let's say we start out with:
int *newArray = new int[1];
And then later have something like:
ifstream inputFile("File.txt");
Counter=0;
while (inputFile >> newValue)
{
newArray[Counter] = newValue;
Counter++
}
If I try to pull 100 lines from the text file, the program will eventually crash. However, if I had used
int *newArray = new int[100];
originally, it doesn't crash.
If it's dynamically allocating memory, why does it need an initial value more than 1? That makes no sense to me. Having to define any initial length beyond a small number such as 1 or 10 defeats the whole purpose of dynamic memory allocation...
EDIT: This is for school, we aren't allowed to use vectors yet.
The language will not "dynamically allocate memory" for you. It is your responsibility to allocate and reallocate your arrays so that their sizes are sufficient for your purposes.
The concept of "dynamic allocation" in C++ never meant that memory will somehow allocate itself automatically for you. The word "dynamic" in this context simply means that the parameters and lifetime of the new object are determined at run time (as opposed to compile time). The primary purpose of dynamic memory allocation is: 1) to manually control object's lifetime, 2) to specify array sizes at run-time, 3) to specify object types at run-time.
The second point is what allows you to do this
int n = ...; // <- some run-time value
int *array = new int[n];
which is not possible with non-dynamically allocated arrays.
In your example, you can allocate an array if size 1 initially. Ther's nothing wrong with it. But it is still your responsibility to allocate a new, bigger array, copy the data to the new array and free the old one once you need more space in your array.
In order to avoid all that hassle you should simply use a library-provided resizable container, like std::vector.
It's not dynamic in the sense that it can dynamically resize itself. It's dynamic in the sense that its size can be chosen dynamically at runtime, instead of compile time. One of the primary philosophies of C++ is that you don't pay for what you don't use. If dynamic arrays worked the way you are asking, that would require bounds checking, something I don't need, so I don't want to pay for it.
Anyway, the problem is solved with the standard library.
std::vector<int> vec;
...
while (inputFile >> newValue)
{
vec.push_back(newValue);
}
Isn't that much nicer? You don't even have to keep track of the size, because vector keeps track of it for you.
If you can't use vector, then you've got a lot of work ahead of you. The principle is essentially this. You keep 2 additional integer variables. One to indicate the number of values you are using in your array, and one to indicate the current capacity of your array. When you run out of room, you allocate more space. For example, here is a poor man's non-exception safe version of a vector:
int size = 0;
int capacity = 1;
int array = new int[capacity];
while (inputFile >> newValue)
{
if (size == capacity)
{
capacity *= 2;
int * newArray = new int[capacity];
for (int i=0; i<size; ++i)
newArray[i] = array[i];
delete [] array;
array = newArray;
}
array[size++] = newValue;
}
You're only creating space for one int but trying to store several, of course it crashes. Even if you created it with size 100 it'd still crash when you tried to save the 101'th value.
If you need an automatically resizing container check out std::vector.
#include <vector>
std::vector<int> data;
while (inputFile >> newValue)
{
data.push_back(newValue);
}
This will work until your process runs out of memory.
I'm writing a simulation for class, and part of it involves the reproduction of organisms. My organisms are kept in an array, and I need to increase the size of the array when they reproduce. Because I have multiple classes for multiple organisms, I used a template:
template <class orgType>
void expandarray(orgType* oldarray, int& numitems, int reproductioncount)
{
orgType *newarray = new orgType[numitems+reproductioncount];
for (int i=0; i<numitems; i++) {
newarray[i] = oldarray[i];
}
numitems += reproductioncount;
delete[] oldarray;
oldarray = newarray;
newarray = NULL;
}
However, this template seems to be somehow corrupting my data. I can run the program fine without reproduction (commenting out the calls to expandarray), but calling this function causes my program to crash. The program does not crash DURING the expandarray function, but crashes on access violation later on.
I've written functions to expand an array hundreds of times, and I have no idea what I screwed up this time. Is there something blatantly wrong in my function? Does it look right to you?
EDIT: Thanks for everyone's help. I can't believe I missed something so obvious. In response to using std::vector: we haven't discussed it in class yet, and as silly as it seems, I need to write code using the methods we've been taught.
You need to pass oldarray as a reference: orgType *& oldarray. The way it's currently written, the function will delete the caller's array but will not give it the newly allocated one, causing the crash.
Better yet, use std::vector instead of reimplementing it.
The C++ standard library already has functionality written to do this.
Use the std::vector container.
Looks like you are modifying the pointer oldarray to point to the new array, but remember in the function that's just a copy and won't affect the variable you passed in. You probably need to pass a reference to a pointer if you want to do it this way.
And indeed, std::vector does this for you anyway
I have this snippet of code which I am considering to simplfy:
if (numberOfResults > 1)
{
trackResult_ = new TrackResult[numberOfResults];
for (int i=0; i < numberOfResults; i++)
{
// Make a deep copy
TrackResult tempResult = result[i];
TrackResult * clone = new TrackResult(tempResult);
trackResult_[i] = *clone;
}
storeJointResults(trackResult_, numberOfResults);
}
else
{
trackResult_ = new TrackResult(*result);
}
(I have 'no choice' but to use a simple dynamic array here. Vectors are deemed 'too complicated' at my workplace)
I am wondering if I can get away with
// even if we just have one result, we init an array size of one
trackResult_ = new TrackResult[numberOfResults];
However, I have in several points check for the number of results and act accordingly
if (numberOfResults_ == 1)
{
velocity = trackResult_.velocity;
}
Would those code still work? If not, why?
The array of size 1 does not need to be a special case.
When you allocate a dynamic array you are given a pointer to the first element. If the array is of size 1, this is pretty much indistinguishable from just having allocated a single instance of the object.
Your special case usage would work if you changed the . to an ->
However I'd recommend not special-casing it and just use trackResult_[0].velocity
No, you need to ensure you match the correct scalar delete or array delete[] depending on whether you say new TrackResult[n]; or new TrackResult;.
Also, this leaks memory for each loop iteration:
TrackResult tempResult = result[i];
TrackResult * clone = new TrackResult(tempResult);
TrackResult_[i] = *clone;
How are vectors too complicated? If anything, the simplify your code.
I agree with Alex, using the . operator on a pointer is not my recommended style either, so those other points ought to be changed anyhow, and thus not discourage you from simplifying the piece of code you mention.