C++ Deleting part of dynamic array - c++

Say I have a dynamic array like:
int* integers = new int[100];
Is there a way to delete only part of the array such as:
int* integers2 = integers + 50;
delete[] integers2;
I want to not only delete everything 50 and beyond, but if I call another delete[] on the original integers array, then it would only delete the correct amount of memory and not try to delete the originally allocated amount and seg fault.
Why I want to do this: I have a data structure that is structured in levels of arrays, and I want to be able to create this data structure from a full array. So I want to be able to say
int* level1 = integers;
int* level2 = integers + 50;
int* level3 = integers + 100;
But when level 3 is no longer needed, the data structure will automatically delete[] level3. I need to know that this will behave correctly and not just destroy everything in the array. If it will then I need to just create new arrays and copy the contents over, but it would be nice to avoid doing that for performance reasons.
Edit: Everyone seems to be jumping to the conclusion that I just should use a dynamic resizing container (ie vector, deque) in the first place for my data structure. I am using levels of arrays for a good reason (and they aren't equally sized like I make it look like in my example). I was merely looking for a good way to have a constructor to my data structure that takes in an array or vector and not need to copy the original contents over into the new data structure.

No, this will not behave correctly. You can only delete[] pointers that you got from new[], else the results are undefined and bad things might happen.
If you really need the array to get smaller you have to allocate a new one and copy the contents manually.

Typically when memory gets allocated, there is some housekeeping stuff before the pointer.
i.e. houskeeping (pointer) data
You will mess that up.

int* integers2 = integers + 50;
delete[] integers2;
Will not work because new is created on int*, so space of 100 int has been assigned to integers, now integers2 is only a pointer to 50th location of integers, it has no space assigned to it of its own, so using delete will not delete rest of integers2, it'll only give erratic results.
What you can do is copy the first 50 in another array, and delete the previous array completely.
delete will only delete the pointer which has space assigned to it, using delete to another pointer pointing to the space assigned to first pointer will not delete any space assigned to the first pointer.
delete[] integers2 will not delete any space assigned to integers1 or any other pointer.

Dynamic allocators (like new) generally don't like you releasing part of the memory they gave you. If you use the <malloc.h> defined library functions malloc() and free() instead of new and delete, then you can use realloc(), though in most cases that you would care about the size difference it's just going to copy for you anyway.
Dynamically sizing containers generally use an exponential rule for resizing: if you run out of space, they (for example) double the allocation (and copy the old data over), if you remove data until you are using (for example) less than half the allocation they copy into a smaller allocation. This means you never waste more than half the memory, and the cost of copying per element added or removed is effectively constant. Implementing all of this is a pain in the ass, though, so just use std::vector and let it do it for you :).

No you can't do this with a fixed sized array allocated with new[]. If you want to have a dynamic array use one of the STL containers, such as std::vector.

Related

Can you delete a C-array of 1 integer as you would a normal integer?

If I have a C-array T* name = new T[1];, can I delete it like this: delete name; since it only has 1 element, or is it undefined behaviour?
Or, to generalize the question, when you have an array T* arr = new T[x];, if you delete arr;, does it only delete the first element of the array, or does it depend from compiler to compiler?
You have to delete it with delete[] name.
You always must match new with delete and new[] with delete[]. Mixing them will result in undefined behaviour.
T arr[x];
is something else entirely, it's an array with automatic storage duration. The memory will be automatically released when it goes out of scope. Trying to delete it will result in undefined behaviour.
And lastly there is no such thing as "deleting only the first element", when you allocate a block of memory, no matter how large it is, you muss deallocate the whole block.
Anyway, that's were standard containers like std::vector come in to make your life easier and hide all the complexity of memory (de-)allocation.
In order for the compiler to be able to use delete[] on an array, it would need to invoke the destructor of every element. This would require that it to know not only the address of the first element (which it would get from the pointer) and the stride (which can be inferred from the element type), but also the number of elements. To accommodate this, it is necessary to allocate space not only for the elements, but also for some kind of integer value indicating how many there are. Thus, new[] would typically allocate slightly more space than needed to hold all the array elements, stores the count at the start of that space, and returns a pointer the portion of the allocation after the count. Calling delete[] would subtract the size of the header from the supplied pointer, use the resulting pointer to retrieve the count, call the destructor on the appropriate number of array elements, and then delete the allocation that contained all of them.
While the exact details of how the count is stored will vary among different implementations, an understanding of the general principle should make it clear why the Standard makes no attempt to specify the effects of combining array new[] and non-array delete, or combining non-array new and array delete[].

Why can't I delete pointer alone from an array of objects?

A* p = new A[5];
delete &p[2];
// error,why? Does it need to use delete []p to delete together?
p is a pointer(p == &p[0]),however, p[0],p[1]... is a type of object. why p[n] is not pointer?
What you delete has to exactly match what you allocated, you can't do partial deletions. The memory manager generally records information about the allocation based on the address that it returned. If you pass a different address to delete, it won't be able to find the allocation information.
What would that achieve? You're trying to free the space of a single A, somewhere in the middle of a longer contiguous array of A's.
This simply doesn't work because it's against what delete does.
when you do new[], your runtime gets you a sufficiently large set of memory to store the array of objects. If that memory isn't "lying around" anyways, the runtime asks the operating system to map memory pages into your processes' memory space.
Now, if you no longer use these pages, you can "give them back"; but, you must do that to the whole chunk of memory you got. That's because freeing less than a page can't work (because the OS can't assign it anywhere else), and because allowing for this kind of fragmentation would make page tables huge.
this is reflected in the documentation of free: You can only free memory by handing free the exact same address malloc gave you.
Yes and no:
Yes, a call to new[] must be paired with a single call to delete[].
No, that does not "delete together", as the result is just one entity (an array), not a collection of As. Therefore, you do not actually delete anything together.
No, you need not end the lifetime of all elements of the array at the same time. However, explicitly calling an destructor is something you might wish to leave to standard library components at this point.
Which brings us to:
What you seem to want to achieve:
End the lifetime of a single object of that array. Since it does not make any sense for the result to be an array with some kind of magic hole, you might want to consider std::vector<A>::erase to deal with it. This will remove one element and move up all other elements so that the result behaves like an array where one element has been removed without leaving a hole.
delete works on pointers (and only pointers to dynamically allocated memory blocks) but you don't have an array of pointers, so delete doesn't work on the array elements, it can only work on the array pointer. And it should be the delete [] operator in particular.
If you want to remove an element from the array, you must iterate the array, and when you reach the element you want to remove, start overwriting the array with the next element. Also, you might want to allocate a new, shorter array, and do that operation there, then delete the old array and set the array pointer to the new array.
Ideally, you should use an std::vector class, which does everything for you automatically:
std::vector<A> v(5); // gives you a vector of 5 A elements
v.erase(v.begin() + 2); // erases the element at index 2
This way is less prone to errors and is preferred, and is just as fast and efficient as the error-prone manual way.
Now if you had an array/vector of pointers to elements, that would be a different matter, then you'd have to make sure you delete the element and erase it from the vector. Naturally, if you use smart pointers, you can simply erase the pointer from the vector and it will be automatically deleted.
why p[n] is not pointer?
The [n] operator for pointers is just a shortcut to deference a pointer with an offset. So p[2] is equivalent to *(p + 2). And when you dereference a pointer, you get a reference to whatever it points to, in your case an A instance. So delete p[2] can only work if it is an array of A pointers, not A instances. Note that in your case &p[2] will actually be a pointer, it will be the same value as p + 2 because the & operator will take the address of the reference, but you should not use delete on it, as it itself doesn't point to dynamcally allocated memory, even if it itself happens to be in such memory.
As you yourself noted in the question p == &p[0] but nonetheless, p != p[0]. This means neither should you delete &p[0] even if both are pointers to the same address. Since the memory for the array has been allocated with new [] it should be deallocated with delete [], and you should not use the delete operator on the array or any of its elements.
&p[0] gives you the address of the first element in the array, which coincides with the beginning of the array, but p == &p[0] is just a value comparison. p may be "equal" to &p[0] but it is not the same semantically, as p is a pointer to A [] and &p[0] is a pointer to A.

Dynamic memory allocation, C++

I need to write a function that can read a file, and add all of the unique words to a dynamically allocated array. I know how to create a dynamically allocated array if, for instance, you are asking for the number of entries in the array:
int value;
cin >> value;
int *number;
number = new int[value];
My problem is that I don't know ahead of time how many unique words are going to be in the file, so I can't initially just read the value or ask for it. Also, I need to make this work with arrays, and not vectors. Is there a way to do something similar to a push_back using a dynamically allocated array?
Right now, the only thing I can come up with is first to create an array that stores ALL of the words in the file (1000), then have it pass through it and find the number of unique words. Then use that value to create a dynamically allocated array which I would then pass through again to store all the unique words. Obviously, that solution sounds pretty overboard for something that should have a more effective solution.
Can someone point me in the right direction, as to whether or not there is a better way? I feel like this would be rather easy to do with vectors, so I think it's kind of silly to require it to be an array (unless there's some important thing that I need to learn about dynamically allocated arrays in this homework assignment).
EDIT: Here's another question. I know there are going to be 1000 words in the file, but I don't know how many unique words there will be. Here's an idea. I could create a 1000 element array, write all of the unique words into that array while keeping track of how many I've done. Once I've finished, I could provision a dynamically allocate a new array with that count, and then just copy the words from the initial array to the second. Not sure if that's the most efficient, but with us not being able to use vectors, I don't think efficiency is a huge concern in this assignment.
A vector really is a better fit for this than an array. Really.
But if you must use an array, you can at least make it behave like a vector :-).
Here's how: allocate the array with some capacity. Store the allocated capacity in a "capacity" variable. Each time you add to the array, increment a separate "length" variable. When you go to add something to the array and discover it's not big enough (length == capacity), allocate a second, longer array, then copy the original's contents to the new one, then finally deallocate the original.
This gives you the effect of being able to grow the array. If performance becomes a concern, grow it by more than one element at a time.
Congrats, after following these easy steps you have implemented a small subset of std::vector functionality atop an array!
As you have rightly pointed out this is trivial with a Vector.
However, given that you are limited to using an array, you will likely need to do one of the following:
Initialize the array with a suitably large size and live with poor memory utilization
Write your own code to dynamically increase the size of the array at run time (basically the internals of a Vector)
If you were permitted to do so, some sort of hash map or linked list would also be a good solution.
If I had to use an array, I'd just allocate one with some initial size, then keep doubling that size when I fill it to accommodate any new values that won't fit in an array with the previous sizes.
Since this question regards C++, memory allocation would be done with the new keyword. But what would be nice is if one could use the realloc() function, which resizes the memory and retains the values in the previously allocated memory. That way one wouldn't need to copy the new values from the old array to the new array. Although I'm not so sure realloc() would play well with memory allocated with new.
You can "resize" array like this (N is size of currentArray, T is type of its elements):
// create new array
T *newArray = new T[N * 2];
// Copy the data
for ( int i = 0; i < N; i++ )
newArray[i] = currentArray[i];
// Change the size to match
N *= 2;
// Destroy the old array
delete [] currentArray;
// set currentArray to newArray
currentArray = newArray;
Using this solution you have to copy the data. There might be a solution that does not require it.
But I think it would be more convenient for you to use std::vectors. You can just push_back into them and they will resize automatically for you.
You can cheat a bit:
use std::set to get all the unique words then copy the set into a dynamically allocated array (or preferably vector).
#include <iterator>
#include <set>
#include <iostream>
#include <string>
// Copy into a set
// this will make sure they are all unique
std::set<std::string> data;
std::copy(std::istream_iterator<std::string>(std::cin),
std::istream_iterator<std::string>(),
std::inserter(data, data.end()));
// Copy the data into your array (or vector).
std::string* words = new std::string[data.size()];
std::copy(data.begin(), data.end(), &words[0]);
This could be going a bit overboard, but you could implement a linked list in C++... it would actually allow you to use a vector-like implementation without actually using vectors (which are actually the best solution).
The implementation is fairly easy: just a pointer to the next and previous nodes and storing the "head" node in a place you can easily access to. Then just looping through the list would let you check which words are already in, and which are not. You could even implement a counter, and count the number of times a word is repeated throughout the text.

If pointers can dynamically change the size of arrays at run time, why is it necessary to initialize the array with a size?

For instance:
int* pArray;
pArray = new array[];
instead of:
int* pArray;
pArray = new array[someNumber];
Since pointers are able to dynamically change the size of an array at run time, and the name of the pointer points to the first element of an array, shouldn't the default size be [1]? Does anyone know what's happening behind the scene?
Since pointers are able to dynamically change the size of an array at run time
This is not true. They can't change the size unless you allocate a new array with the new size.
If you want to have an array-like object that dynamically changes the size you should use the std::vector.
#include<vector>
#include<iostream>
...
std::vector<int> array;
array.push_back(1);
array.push_back(2);
array.push_back(3);
array.push_back(4);
std::cout << array.size() << std::endl; // should be 4
When you create an array with new, you are allocating a specific amount of memory for that array. You need to tell it how many items are to be stored so it can allocate enough memory.
When you "resize" the array, you are creating a new array (one with even more memory) and copying the items over before deleting the old array (or else you have a memory leak).
Quite simply, C++ arrays have no facility to change their size automatically. Therefore, when allocating an array you must specify it size.
Pointers cannot change an array. They can be made to point to different arrays at runtime, though.
However, I suggest you stay away from anything involving new until you have learned more about the language. For arrays changing their size dynamically use std::vector.
Pointers point to dynamically allocated memory. The memory is on the heap rather than the stack. It is dynamic because you can call new and delete on it, adding to it and removing from it at run time (in simple terms). The pointer has nothing to do with that - a pointer can point to anything and in this case, it just happens to point to the beginning of your dynamic memory. The resizing and management of that memory is completely your responsibility (or the responsibility of the container you may use, e.g. std::vector manages dynamic memory and acts as a dynamic array).
They cannot change the size dynamically. You can get the pointer to point to a new allocation of memory from the heap.
Behind the scenes there is memory allocated, a little chunk of silicium somewhere in your machine is now dedicated to the array you just newed.
When you want to "resize" your array, it is only possible to do so in place if the chunk of silicium has some free space around it. Most of the times, it is instead necessary to reserve another, bigger, chunk and copy the data that were in the first... and obviously relinquish the first (otherwise you have a memory leak).
This is done automatically by STL containers (like std::vector or std::deque), but manually when you yourself call new. Therefore, the best solution to avoid leaks is to use the Standard Library instead of trying to emulate it yourself.
int *pArray = new int; can be considered an array of size 1 and it kinda does what you want "by default".
But what if I need an array of 10 elements?
Pointers do not have any magical abilites, they just point to memory, therefore:
pArray[5] = 10; will just yield a run-time error (if you are lucky).
Therefore there is a possibility to allocate an array of needed size by calling new type[size].

adding element to an array

How can I add an element to an array where the size of the array is unknown and vectors are prohibited?
If the size of the array is unknown how do you know where to put the element and whether it will fit?
Anyway, if it won't fit you have to allocate a new array that is big enough.
If you allocated originally with malloc rather than new[] you can use realloc. You might be surprised to know that malloc / realloc are not "replaced" by new but are used for a different purpose, and in this case it is a useful thing to use. You can then insert objects into the allocated memory using placement new, and vector works this way. (allocator is used to actually allocate the memory but has an interface like malloc and although the implementation is up to the library author, they will almost certainly use malloc).
If you reallocated using realloc, you need to know that:
Any memory will be copied over. Beware though that if they are non-POD objects stored it is not safe to just do byte-by-byte copy
If realloc fails it returns NULL and your previous array is not freed. Be certain to keep a pointer to the old location until you know realloc worked.
If your array is not POD you cannot realloc, so malloc the new memory and use placement-new with a copy-constructor, then call the destructor on each object of the old memory before freeing it.
Placement new is used so you can allocate more memory than you need for now, i.e. more than you have objects for. This prevents you having to go through this process every single time you append.
I have explained how you might implement vector, but this is probably far too complex for you and probably for your tutor too. So just create an array with new[] and copy over the elements although it is horribly inefficient if you have to do this every time you add one.
You use lists for this purpose. An array is not supposed to be extended.
Your question does not make sense. If you can't use vector, means this is an assignment. I assume you have to use native arrays. If you don't know the size, you just cannot add an element to it. If you knew the size, you would have to allocate a bigger array, copy the contents of the old array plus the new element. Which the vector actually does for you :)
Without knowing the current size of the array and the capacity of the array (the distinction there is important), adding elements to the array is dangerous. You will never know when you have passed the bounds of the array (at least not before your program crashes from a buffer overrun error).
If your professor has banned vector for an assignment, chances are he wants to show you how to manage memory. He's looking for you to create an array using new and properly recognize when you need to allocate a new array of a larger size, copy the original elements, and deallocate the original array using delete.
One hint most professors fail to mention: when you allocate an array using new [], you must also deallocate it using delete [].