Hi there i have some questions about pointers and arrays in c++:
when i want to pass an array of pointers in a function i must write:
foo(test *ptest[])
right?
when i want to delete a array of pointers, does
delete ptest;
all for me?
when i pass a pointer to another class and the first class make a delete, the pointer is deletete in all classes right?
must i always create a array of a constant size?
First of all, forget about arrays and pointers and tell us what you really want to achieve. You are probably better off using std::vector<T> and pass that around by (const) reference, saving you all the headaches of manual resource management and crazy array-to-pointer-decay rules inherited from C.
Now to answer your questions:
when i want to pass an array of pointers in a function i must write:
foo(test *ptest[])
right?
Yes, but remember that array parameters are always rewritten by the compiler to pointers. Your code is equivalent to:
foo(test **ptest)
This means that all information about the size of the array has been lost. Moving on:
when i want to delete a array of pointers, does
delete ptest;
all for me?
No. If you acquire via new[], you must release via delete[], otherwise you get undefined behavior. Note that deleting an array of pointers only gets rid of the array itself, not any objects pointed to by the pointers stored inside the array.
Again: Forget about arrays and pointers and use the facilities provided by the C++ standard library.
when i want to delete a array of pointers, does delete ptest; all for me?
Nope. It tries to delete what's been allocated to the address ptest. If ptest is not something which was allocated dynamically, your program will crash. You can only call delete on the addresses which you have received from new.
when i pass a pointer to another class and the first class make a delete, the pointer is deletete in all classes right?
Nope, you need to have exactly one delete for every new. (yah, there are "smart pointers" which call delete for you, but the point still holds.)
must i always create a array of a constant size?
Nope. In C++ there's std::vector for having arrays of dynamic size (can be used for constant-size arrays too) and in C there's realloc for changing a size of an array.
To delete any array you need:
delete [] ptest; // note the [] part
Arrays can be any size you like (within various practical limits) and unless you make copies of an array then you should delete it exactly once (it doesn't matter where you do this, so long as you don't try to acess the data afterwards).
Chances are, without knowing what you're aiming at and any other contextual information, you should use std::vector<T> instead and forget about these worries.
That said, to delete any primitive array arr you must say
delete[] arr;
Of course, if the pointers in the array themselves are each the last pointing at heap memory then you should free them first. You can do that nicely with a reusable Delete functor:
struct Delete {
template<class P>
void operator()(P ptr) const { delete ptr; }
};
// ...
std::foreach(arr, arr+ARR_LENGTH, Delete());
delete[] arr;
You must of course make sure no two pointers point at the same address. To further simplify things beyond what a std::vector<T> can do for you you should consider using a boost::shared_ptr<T>; this will allow you to forget about the delicate case in which two pointers point at the same address.
Related
Consider the following code. This works for allocating a single array, but what if you needed to allocate a two dimensional array? How would you go about doing this?
#include <iostream>
void alloc(int **num)
{
*num = new int[5];
*num[0] = 5;
}
int main()
{
int *num;
alloc(&num);
std::cout << num[0] << std::endl;
delete [] num;
}
My goal is to pass **char into the function and have it allocated. Is this possible?
what if you needed to allocate a two dimensional array?
My goal is to pass **char into function and have it allocated.
So, what you want is a pointer to an array of pointers (each to an array)? That is different from an 2D array, although often treated as such for simplicity.
Just like you added an additional layer of indirection when you allocated that single array, you can add a layer of indirection to allocate an array of pointers, and the pointed to arrays of integers.
So, you could write a function like void alloc(int ***num). Yes, this is possible, but not very good design.
But using a pointer for the indirection makes the syntax more complex, and has the risk of a user passing a null pointer to the function. Instead, a better alternative would be to use a reference: void alloc(int **&num).
Since the function allocates something and "returns a pointer", it would make more sense to actually return the pointer: int** alloc(). Much prettier, isn't it? Less indirection is better.
Finally, there is the problem that a user of the function has to know how to delete the allocated memory. Was it allocated with new or new[]? Maybe even malloc? Or does the function perhaps return a pointer to a static object, that must not be deleted? The caller of the function cannot know that unless it is documented separately.
Also, what if one of the allocation fails? There will be an exception, and the earlier allocations will leak.
The C++ way to solve the above 2 problems is to use a RAII container to manage the memory. Since you apparently want to have an array with pointers to arrays, there is a ready made solution in the standard library for you: return a std::vector<std::vector<int>>. If you instead want a proper non-jagged 2D array i.e. a matrix, then you may want to write a custom RAII container for that, possibly using a flat std::vector<int> for the implementation. Note that a third party may already have implemented such container.
Your code looks like C to me. In C++ I would (maybe) use custom types and not do any manual dynamic allocation. For example
typedef std::vector<int> Row;
typedef std::vector<Row> Matrix;
Matrix initMatrix(int nRows,int nCols) {
return Matrix(nRows,Row(nCols));
}
well in that case you dont even need a function but you can just call the constructor (and let array allocate the memory for you).
For example i have class with constructor that has array of ints as parameter:
A(int* array) : m_array(array) {}
I can use it like this:
int array[] = { ... }
A a(array);
Or like this:
int* array = new int[10];
A a(array);
If object then use it array, it must (or may be not?) delete it in desctructor (if it was dynamic). But how he will know, that memory for this array was allocated dynamically?
You can't know if it's dynamically allocated or not, because after all, int* array is an int pointer, not an array. You might as well pass:
int i;
A a(&i);
As you can imagine, bad things will happen if you try to delete[] that one, or try to access m_array[N] with N > 0.
So you have to rely on the caller to do the right thing; there's nothing you can do to verify or enforce it. All you have is the address of an int. Who created that int, or how or whether more ints follow after it, will be unknown.
If you want more safety, use an std::vector. This is what it was made for.
You're initializing the array in the constructor, so it will always be initialized. It is pre-defined in the code that it will be allocated. If there are other constructors that do not allocate your array, you will need to do this check.
By the way, this is under the assumption that the array you are allocating is a member of the class. If you are assigning it to a new stack variable within the constructor, you won't be able to delete it in the destructor.
From what i understand you are trying to ask is whether the destructor will free the memory you have allocated to the array.
No, the memory you have allocated using new will have to be deleted by you either in the destructor or somewhere else the pointer is in scope as your memory allocation is not inside the constructor but outside.
You cannot know what it was because a static array decays into a pointer as well.
Basically you just need the value of the array passed to the constructor. You need not know whether it was a dynamically allocated or statically allocated array. What matters is the data member array which is part of the interface of your class and into which you are copying the data. Responsibility of the array passed to the constructor as an argument should rest with the caller regarding its deletion and lifetime.
It would make your life easier if you use std::vector instead of raw array.
Say I have two items on the heap:
Foo *f = new Foo;
Foo *g = new Foo[42];
And say I have a function that receives a Foo pointer and within the function it needs to perform a delete:
void bar(Foo *p) {
// some stuff
delete p;
}
This function might be called like so:
bar(f); // passing a pointer to a Foo object on the heap
bar(g); // passing a pointer to an array on the heap
I recognize that delete[] and delete should be used to free memory allocated with new[] and new respectively; However since the function doesn't know if its parameter p was allocated with new or new[], how can this function properly delete or delete[]?
It sounds like you have a problem with the Single Responsibility Principle.
You have some amount of processing that operates on a single object, so it's reasonable to pass an individual object or an array. (Why isn't that processing also useful for an array element other than the first, or a non-dynamic object?)
And then you have to free the object. Or the array.
There are three different tasks here, which calls for three different functions.
Essentially, if "the function doesn't know if its parameter p was allocated with new or new[]", that function has no business trying to deallocate. What if the parameter is on the stack? What if a pooled allocator was used?
Also, once the processing is moved to a separate function, it becomes easy to make a function for "process then delete single object" and also "process then delete array" without duplication (both call the helper function for the processing component).
You can't (portably) detect that and do the right thing in the function.
A "workaround" would be to use std::vector<Foo> instead of the array, and just always use delete.
Solution 1. From a code design point of view, you should probably avoid cases like this altogether, and have the object be deleted by the same class, pair of create-destroy functions, or code block that allocated it. This way, you know you're not making any mistakes.
Solution 2. If you compiler supports them, use std::shared_ptr and lambda functions, and use a different deallocator for each pointer, as appropriate. Read about shared pointers here.
std::shared_ptr<Foo> f(new Foo[20], [](Foo* p) { delete[] p; });
std::shared_ptr<Foo> g(new Foo);
The first one, f is a shared pointer to an array. When I create it, I give as the first parameter a (normal) pointer to the array; the second parameter is a lambda function that takes a Foo* p parameter and its body is { delete[] p; }.
By default, std::shared_ptr uses delete to deallocate memory. This is why, when I create the second one, I only give it the pointer to the object and don't specify any custom deallocator.
You can't. You never know if a pointer points to an object or an array of such object.
But, if you really want to have a single function for deallocation, and if you can ever ever remember to allocate anything like an array even if you're allocating one object, the following use is legal:
Foo *h = new Foo[0];
Foo *f = new Foo[1];
Foo *g = new Foo[42];
void bar(Foo *p) {
// some stuff
delete [] p;
}
However, remember that sometimes technically possible doesn't mean that you should use it. That depends on your use case whether it makes sense to treat an object as a special case of an array.
The more elegant C++ way is to use std::vector or boost::scoped_ptr, boost::scoped_array, etc, which guarantee to call correct version of delete operator, just as their names indicate.
You have to pass which it is. The function can't determine that at runtime.
See here for a good explanation.
It's up to the compiler vendor to decide how this should be implemented, and as such falls under the "magic" category.
In general though, the runtime will allocate an extra additional 4 bytes when you use new[]. Then it will store the size of the array in these bytes, and return the allocation + 4 bytes. When deallocating, it will remove 4 from the pointer you passed it, read the size and use that to decide how many destructors etc. to call.
You can read more about this in the C++ FAQ
How do smart pointers handle arrays? For example,
void function(void)
{
std::unique_ptr<int> my_array(new int[5]);
}
When my_array goes out of scope and gets destructed, does the entire integer array get re-claimed? Is only the first element of the array reclaimed? Or is there something else going on (such as undefined behavior)?
It will call delete[] and hence the entire array will be reclaimed but I believe you need to indicate that you are using an array form of unique_ptrby:
std::unique_ptr<int[]> my_array(new int[5]);
This is called as Partial Specialization of the unique_ptr.
Edit: This answer was wrong, as explained by the comments below. Here's what I originally said:
I don't think std::unique_ptr knows to call delete[]. It effectively
has an int* as a member -- when you delete an int* it's going to
delete the entire array, so in this case you're fine.
The only purpose of the delete[] as opposed to a normal delete is that
it calls the destructors of each element in the array. For primitive
types it doesn't matter.
I'm leaving it here because I learned something -- hope others will too.
I have a struct like this:
class Items
{
private:
struct item
{
unsigned int a, b, c;
};
item* items[MAX_ITEMS];
}
Say I wanted to 'delete' an item, like so:
items[5] = NULL;
And I created a new item on that same spot later:
items[5] = new item;
Would I still need to call delete[] to clean this up? Or won't this be needed since bounds of array items[] are known before compiling?
Is setting that pointer to NULL valid or should I be calling delete there?
You need to call delete before setting it to NULL. (Setting it to NULL isn't required, it just helps reduce bugs if you accidentally try to dereference the pointer after deleting it.)
Remember that every time you use new, you will need to use delete later on the same pointer. Never use one without the other.
Also, new [] and delete [] go together in the same way, but you should never mix new [] with delete or new with delete []. In your example, since you created the object with new (rather than new [] which would create an array of objects) you must delete the object with delete (rather than delete []).
As Kluge pointed out, you'd leak the object at index 5 like that. But this one really sounds like you shouldn't do this manually but use a container class inside Item. If you don't actually need to store these item objects as pointers, use std::vector<item> instead of that array of MAX_ITEMS pointers. You can always insert or erase vector elements in the middle as well if you need to.
In case you need to store the objects as pointers (usually if struct item is actually polymorphic, unlike in your example), you can use boost::ptr_vector<item> from Boost.PtrContainer instead.
Example:
class Items {
private:
struct item {
unsigned int a, b, c;
};
std::vector<item> items;
}
if (items.size() > 5) // (just to ensure there is an element at that position)
items.erase(items.begin() + 5); // no need to use operator delete at all
To delete an item use:
delete items[5];
after deleting the item it is advisable to set the deleted pointer to NULL, so you won't have an error if you later delete it again by mistake.
items[5] = NULL
Say I wanted to 'delete' an item, like so:
items[5] = NULL;
I know little Visual Basic, but that smells like a Visual Basic programming idiom, since "Set a = None" (or Null, I'm not sure) would delete the object pointed by a (or rather decrement its reference count, for COM objects).
As somebody else noted, you should use either:
delete items[5];
items[5] = newContent;
or:
delete items[5];
items[5] = NULL;
After delete[5], the only possible use of the pointer stored in items[5] is causing you trouble. What's worse is that it might happen to work at the beginning, and start failing only when you allocate something else over the space previously used by *items[5]. Those are the causes which make C/C++ programming "interesting", i.e. really annoying (even for who likes C like me).
Writing just delete items[5]; saves what can be an useless write, but that's a premature optimization.
Just to be clear: you refer to calling "delete[]". I think you mean delete.
I mention this because C++ has two separate operators, operator delete and operator delete[]. The latter is used for deleting arrays of objects allocated with operator new[], and does not apply in this case. You have an array of pointers to objects, which you must have initialised with repeated calls to operator new rather than a single call to operator new[].
All I'm really trying to say is: your use of delete[] is confusing and ambiguous; change it to delete.
There are a few, related, questions here:
According to the code you posted, the array itself is not allocated on the heap unless the struct is, so you don't need to delete[] the array. If you created the array with new[] you would have to delete[] it.
The code posted doesn't say how the objects being pointed to from the array are allocated. If you allocate those objects on the stack you must not delete them (then again, this is highly unlikely because your pointers will become invalid when the objects they point to fall out of scope). If you allocated them on the heap (with new) then you must delete them when they fall out of scope.
As others have already suggested, life is much easier if you use a container -- especially an STL container -- and smart pointers -- which for now means pointers out of Boost.
C++ isn't my strong suit, but I'm pretty sure you'd be leaking the memory if you set the pointer to NULL.
EDIT: The memory being leaked would be the memory being pointed to by the pointer in the array.
Setting items[5] to NULL doesn't delete the memory associated with the item, it simply sets the pointer to that item to NULL, therefore the memory is leaked.
You can delete the item by calling:
delete items[5];
Since C++ has not automatic garbage collection, you need to delete any memory you no longer need.