Deallocating memory while deleting an array in C++ - c++

In C++ when we assign an array there is no way to find out the size of the array once it has been initialized. Then how does the delete operator know the amount of memory to delete when I am trying to deallocate memory at the end of my program.
int main()
{
int* p = new int[10];
int* q = new int[30];
//... bunch of code
//...
// ... bunch of code
delete[] p;
delete[] q;
return 0;
}

The new operator ends up creating an entry on the heap, and the heap allocator knows how to de-allocate things it's previously allocated. This information isn't normally available to your code because it's all C++ internals you're not supposed to mess with.
So basically the heap metadata describes this allocation.
Remember that in C++ you can write your own allocator, so new and delete[] might end up interfacing with that if you so desire. If you look at how std::allocator is defined, note that you're not obligated to tell anyone what allocations have been made, nor how big any particular allocation is. The allocator has a tremendous amount of freedom here, and the specification doesn't allow for a lot of interrogation.

Related

Using delete[] on an array passed as an argument

So let's say that you wanted to make a realloc function in C++. I imagine it can work something like this:
template<typename T>
void realloc(T** arr, int size, int original_size){
T *newArr = new T[size];
for(int i = 0; i<original_size;i++)
newArr[i] = (*arr)[i];
delete[] *arr;
*arr = newArr;
}
Now my question is wheter it is correct to use delete[] here? From what I know allocated memory size is tracked at runtime so C++ knows how much to delete. Is that preserved when passing it like this or are there better ways of doing this?
Now my question is wheter it is correct to use delete[] here?
Yes, as long as *arr was allocated using new[].
From what I know allocated memory size is tracked at runtime so C++ knows how much to delete. Is that preserved when passing it like this [...]?
I'm not really sure what you mean here. The runtime keeps track of memory allocations, yes. But you have to be explicit about using delete[] for arrays (like new int[10]) and delete for non-arrays (like new MyClass()).
or are there better ways of doing this?
Using raw pointers should be avoided, even naked new is considered bad practice these days. Smart pointers and STL-containers like std::vector are superior alternatives for almost every scenario.

Is there a way to expand a dynamic memory array in C++?

Is there a way to expand a dynamic memory array? like this:
int *a = new int[5];
*a = new int[2];
Is this legal?
You cannot expand this type of a dynamic memory array. You can use malloc and realloc though if you need this facility but I would advice against that and suggest including <vector> and using std::vector instead. It has a resize method.
Also, what you described won't compile. The following will:
1: int *a = new int[5];
2: a = new int[2];
The above will allocate two memory blocks, neither of which will be destroyed. Second line will simply assign a new array to the same int *a pointer. When an allocated memory stops being referenced by any pointer, this is called a memory leak. The above code loses any reference to new int[5] and there is no way to free this memory to the operating system.
Although this is not a very practical example, there are multiple ways to resize an array/vector.
As it is usually practical to increase the array size, I will do just this:
{ // C++ vector on the stack (although internally vector uses memory from the heap)
std::vector<int> a(1024);
// do smth
a.resize(4096); // note: this does not always reallocate
// do smth else
}
{ // C++ everything on the heap
std::vector<int> *a = new std::vector<int>(1024);
// do smth
a->resize(4096); // note: this does not always reallocate
// do smth else
delete a;
}
{ // C style
int *a = (int*)malloc(1024*sizeof(int));
// do smth
a = realloc(a, 4096*sizeof(int));
// do smth else
free(a);
}
It is worth to note that realloc does not do anything smart. All it does is:
Allocate new memory block malloc
Copy data from old memory block to new memory block memcpy
Free old memory block free
Return new memory block
You can certainly expand an array, but you need to take care of copying the contents and of freeing the old array (your code, apart from being incorrect syntax, shrinks the array, btw.).
Which is exactly how std::vector works, just you don't have to care.
So basically, having int *a already allocated, what needs to happen is something like:
{
std::unique_ptr<int[]> d(a);
a = new int[desired_new_size];
for(unsigned int i = 0; i < min_old_size_and_new_size; ++i)
a[i] = d[i];
}
Note that strictly speaking "expanding" never really expands the array, but replaces it with another bigger one (that is true for any containers offering the same functionality too). But this is transparent to any code using the pointer later, nobody will know.
You should never use realloc (or any other C memory allocation functions) in combination with memory allocated or freed by operator new and delete (or new[] and delete[]) as pointed out above.
This may work (and usually will), but it's conceptually wrong, and it's pure luck (unknown implementation detail) if it does not crash.

Dynamically allocating memory on stack

There is such code:
#include <iostream>
int main()
{
int a;
int* p = new (&a) int(2);
std::cout << a << std::endl;
// delete p; error BLOCK TYPE IS INVALID
std::cin.get();
return 0;
}
The output is:
2
Why is it possible to dynamically allocate memory on stack? (I thought that heap is the right place to do this). And, why does delete operator return error in this case, but new operator work?
This is using the placement new syntax. Placement new does not allocate memory at all, rather, it is a way to construct an object at a particular location. In this example, the memory comes from the stack. It doesn't have to. delete has issues because you didn't new the memory.
There are ways to dynamically allocate memory from the stack (alloca) but that's not what is happening here.
int* p = new (&a) int(2);
This is called placement-new. It doesn't allocate memory. It constructs the object in the same memory of a. In placement new, it's the user who specifies the memory region where new operator constructs the object. In your code above, you specify the memory region by writing (&a) expression just after the new keyword. Since &a is not a dynamically allocated memory, you cannot delete it:
delete p; //runtime-error
It would give runtime error, it attempts to delete the memory where the variable a resides.
However, if you dynamically allocate the memory, then you can do delete it. Lets suppose, A is some class, then you should be doing this:
char *buffer = new char[sizeof(A)]; //allocate memory of sizeof(A);
///ASSUMPTION: the buffer is properly align as required by the type A
//use placement-new to construct an object at the specified memory region
A *pA = new (buffer) A(/*..parameters..*/);
//...
//incorrect way to delete the memory!
//delete pA; //incorrect
//before deleting the memory you should be calling the destructor explicitly as
pA->~A(); //call the destructor explicitly - must do it
//now deallocate the memory as
delete []buffer;
This is simplest example of placement-new which explains the syntax only. But the story doesn't end here; it is the beginning and to make it work properly, the memory pointed to by buffer has to be aligned properly for the object type, and in the above example, I simply assumed so. In the real code, you cannot make such dangerous assumption. Now read this FAQ:
What is "placement new" and why would I use it?
This is called placement new: http://www.parashift.com/c++-faq-lite/dtors.html#faq-11.10
You can optionally pass an address to new, and it will only call the object's constructor (if it has one). Because no memory was allocated, it would be an error to deallocate it with delete. Simply call the object's destructor (if it has one) and you're done.
C++ separates the notions of memory allocation and object lifetime. This is perhaps one of the most important "new" aspects of the language compared to C. In C there is no such distinction because variables are entirely determined by their memory, while in C++ objects have a more abstract notion of a "state" which is distinct from the underlying memory.
Let's look at memory first:
{
char buf[100]; // automatic allocation, scoped lifetime
}
{
void * p = std::malloc(100); // dynamic allocation, manually managed
void * q = ::operator new(100); // essentially identical
// ...
::operator delete(q); // manual deallocation
std::free(p); // ditto
}
On the other hand, an object's lifetime is a separate topic:
{
Foo x; // automatic storage, scoped lifetime.
// Implies automatic memory allocation for sizeof(Foo) bytes.
}
{
Foo * px = ::new Foo; // dynamic storage, manual lifetime,
// implies dynamic allocation via ::operator new()
Foo * py = ::new (q) Foo; // dynamic storage and manual lifetime, uses memory at q
// ...
delete px; // destroy object _and_ deallocate memory
py->~Foo(); // destroy object. Memory was never our business to start with.
}
As you can see, the separation of memory and object lifetime adds a lot of flexibility: We can have dynamic objects living in automatic memory, or take care of allocation ourselves and reuse memory for repeated object constructions. The standard new and delete expressions combine allocation and construction, but this is only a shortcut for the most frequently used operations. In principle, you're entirely free to handle memory and object lifetime separately.
This idea underpins the notion of allocators, which are a core concept in the C++ standard library.

Is it possible to delete C POD using delete in C++?

Having structs like
struct ifoo_version_42 {
int x, y, z;
char *imageData;
};
where imageData is something like imageData = new char[50000];
Can we perform something like:
template< typename T >
void del( T a ) // we promise to use this only on C Plain Old data structs=)
{
delete a;
}
on this structure an will it be enough to clean memory form if?
Deleting the struct does not recursively delete any pointers in it, and hence doesn't free the array of char pointed to by imageData.
I'm also a bit confused by your use of delete[]. You can free an array (allocated with new[]) using delete[], or free a single object (allocated with new) using delete. You can't mix them, and you don't say how you're allocating one or more instances of ifoo_version_42. For example the following has undefined behavior:
ifoo_version_42 *x = new ifoo_version_42;
del(x);
The following is OK:
ifoo_version_42 *x = new ifoo_version_42[1];
del(x);
This function template would also "work" on non-POD types. It's literally no different from invoking delete[] a; directly.
However, this won't delete the memory associated with imageData. That's typically the sort of thing you do in a destructor.
If you perform your del function on an ifoo_version_42, then the memory block pointed to by data will not be freed; neither delete nor delete[] work recursively.
delete[] is meant to be used for freeing arrays; that is, if you allocated imageData with new[], then it should be freed with delete[].
delete is meant to be used for freeing single objects: If you e.g. allocated a ifoo_version_42 with new, then you should free it with delete.
(Also, never use delete for something allocated with malloc(), or free() with something allocated with new.)
One further suggestion: Learn the RAII idiom and use smart pointer classes provided by the STL or Boost libraries; these go a long way towards helping you with correct memory management.

Resizing Arrays - Difference between two execution blocks?

I have a function which grows an array when trying to add an element if it is full. Which of the execution blocks is better or faster?
I think my second block (commented out) may be wrong, because after doubling my array I then go back and point to the original.
When creating arrays does the compiler look for a contiguous block in memory which it entirely fits into? (On the stack/heap? I don't fully understand which, though it is important for me to learn it is irrelevant to the actual question.)
If so, would this mean using the second block could potentially overwrite other information by overwriting adjacent memory? (Since the original would use 20 adjacent blocks of memory, and the latter 40.)
Or would it just mean the location of elements in my array would be split, causing poor performance?
void Grow()
{
length *= 2; // double the size of our stack
// create temp pointer to this double sized array
int* tempStack = new int[length];
// loop the same number of times as original size
for(int i = 0; i < (length / 2); i++)
{
// copy the elements from the original array to the temp one
tempStack[i] = myStack[i];
}
delete[] myStack; //delete the original pointer and free the memory
myStack = tempStack; //make the original point to the new stack
//Could do the following - but may not get contiguous memory block, causing
// overwritten >data
#if 0
int* tempStack = myStack; //create temp pointer to our current stack
delete[] myStack; //delete the original pointer and free memory
myStack = new int[length *= 2]; //delete not required due to new?
myStack = tempStack;
#endif
}
The second block wouldn't accomplish what you want at all.
When you do
myStack = new int[length *= 2];
then the system will return a pointer to wherever it happens to allocate the new, larger array.
You then reassign myStack to the old location (which you've already de-allocated!), which means you're pointing at memory that's not allocated (bad!) and you've lost the pointer to the new memory you just allocated (also bad!).
Edit: To clarify, your array will be allocated on the heap. Additionally, the (new) pointer returned by your larger array allocation (new int[foo]) will be a contiguous block of memory, like the old one, just probably in a different location. Unless you go out of bounds, don't worry about "overwriting" memory.
Your second block is incorrect because of this sequence:
int* tempStack = myStack; //create temp pointer to our current stack
delete[] myStack; //delete the original pointer and free memory
tempStack and myStack and both simply pointers to the same block of memory. When you delete[] the pointer in the second line, you no longer have access to that memory via either pointer.
Using C++ memory management, if you want to grow an array, you need to create a new array before you delete the old one and copy over the values yourself.
That said, since you are working with POD, you could use C style memory management which supports directly growing an array via realloc. That can be a bit more efficient if the memory manager realizes it can grow the buffer without moving it (although if it can't grow the buffer in place, it will fall back on the way you grow your array in your first block).
C style memory management is only okay for arrays of POD. For non-POD, you must do the create new array/copy/delete old array technique.
This doesn't exactly answer your question, but you shouldn't be doing either one. Generally new[] or delete[] should be avoided in favor of using std::vector. new[] is hard to use because it requires explicit memory management (if an exception is thrown as the elements are being copied, you will need to catch the exception and delete the array to avoid a memory leak). std::vector takes care of this for you, automatically grows itself, and is likely to have an efficient implementation tuned by the vendor.
One argument for using explicit arrays is to have a contiguous block of memory that can be passed to C functions, but that also can be done with std::vector for any non-pathological implementation (and the next version of the C++ standard will require all conforming implementations to support that). (For reference, see http://www.gotw.ca/publications/mill10.htm by Herb Sutter, former convener of the ISO C++ standards committee.)
Another argument against std::vector is the weirdness with std::vector<bool>, but if you need that you can simply use std::vector<char> or std::vector<int> instead. (See: http://www.gotw.ca/publications/mill09.htm)