C++: how does C++ know to destruct smart pointers inside containers? - c++

In The C++ Programming Language, there is the following example (section
3.2.4).
unique_ptr<Shape> read_shape(istream& is);
void user()
{
vector<unique_ptr<Shape>> v;
while (cin)
v.push_back(read_shape(cin));
draw_all(v);
// call draw() for each element
rotate_all(v,45);
// call rotate(45) for each element
} // all Shapes implicitly destroyed
Stroustrup is trying to make the point that by using unique_ptrs, one
doesn't have to manually loop through the vector and delete all
Shapes. I'm having trouble understanding why this is true.
The way I understand smart pointers is that if a smart pointer sptr is
allocated on the stack, with something like
std::unique_ptr<Shape> sptr(new Shape());
you don't need to call delete on it at the end of the function. However,
from what I can tell in the above example those smart pointers are not
on the stack, they're put inside the data array of v, which is on the
heap.

Whether the unique_ptr is on the stack or the heap doesn't really matter. What matters is that it will automatically call delete on the target pointer when its destructor runs.
When the vector goes out of scope, the vector's destructor runs. That destroys all contained elements (calling their destructors in the process) and then deallocates its heap buffer.

This is what destructors are for. You don't see the "manual loop" that destroys the unique pointers because that loop is within the destructor of the vector. All containers destroy their elements.

When the destructor for std::vector runs it calls the destructor for each object it contains. The destructor for std::unique_ptr calls delete/delete[] on the pointer it wraps, so all of the memory that was allocated will be cleaned up correctly.

When a std::unique_ptr itself is destructed or reset, it calls delete or delete[] (or a custom deleter that you specify) on the object/array that it is currently holding a pointer to.
If the std::unique_ptr is created in automatic memory, such as the stack, it is destructed when it goes out of scope.
If the std::unique_ptr is created in dynamic memory, such as the heap, it is destructed when delete is called on it.
A container owns the elements that it stores. It knows whether its elements are of a type that defines a destructor or not, and if so then it will call that destructor on each element when needed, such as when removing individual elements from the container, or when the container itself is destructed or cleared.
So, if you have a container of std::unique_ptr elements, they will be destructed for you when appropriate, which in turn will destruct the things that they are pointing at.

Yes, if you create something on the heap, you have to delete it yourself. Unlike automatic variables (those on the stack, even though the standard doesn't actually mandate the existence of a stack), the lifetime (scope) of those objects can continue across function return boundaries.
However, the vector here is not on the heap - it's actually an automatic variable that will undoubtedly store some of its information on the heap (the array holding the vector elements, most likely), but the variable itself is not.
That means, when it goes out of scope on exit from user(), the destructor is called for it. That destructor is smart enough to free the heap-specific stuff for the vector itself, and call the destructor for each element in the vector.
And, of course, destructing each of those elements may cause further destructors to be called. If one of those elements being destructed is a smart pointer, it will know that it needs to release its hold on the underlying pointer, by either freeing it or decrementing a count to say it has no further interest in it.

Related

Can i delete a statically defined variable using delete operator? [duplicate]

I have an object with a vector of pointers to other objects in it, something like this:
class Object {
...
vector<Object*> objlist;
...
};
Now, Objects will be added to list in both of these ways:
Object obj;
obj.objlist.push_back(new Object);
and
Object name;
Object* anon = &name;
obj.objlist.push_back(anon);
If a make a destructor that is simply
~Object {
for (int i = 0; i < objlist.size(); i++) {
delete objlist[i];
objlist[i] = NULL;
}
}
Will there be any adverse consequences in terms of when it tries to delete an object that was not created with new?
Yes, there will be adverse effects.
You must not delete an object that was not allocated with new. If the object was allocated on the stack, your compiler has already generated a call to its destructor at the end of its scope. This means you will call the destructor twice, with potentially very bad effects.
Besides calling the destructor twice, you will also attempt to deallocate a memory block that was never allocated. The new operator presumably puts the objects on the heap; delete expects to find the object in the same region the new operator puts them. However, your object that was not allocated with new lives on the stack. This will very probably crash your program (if it does not already crash after calling the destructor a second time).
You'll also get in deep trouble if your Object on the heap lives longer than your Object on the stack: you'll get a dangling reference to somewhere on the stack, and you'll get incorrect results/crash the next time you access it.
The general rule I see is that stuff that live on the stack can reference stuff that lives on the heap, but stuff on the heap should not reference stuff on the stack because of the very high chances that they'll outlive stack objects. And pointers to both should not be mixed together.
No, you can only delete what you newed
Object* anon = &name;
When name goes out of scope, you will have an invalid pointer in your vector.
What you're actually asking is whether it's safe to delete an object not allocated via new through the delete operator, and if so, why?
Unfortunately, this is obfuscated by some other problems in your code. As mentioned, when name goes out of scope, you're going to end up with an invalid pointer.
See zneak's answer for why your original question doesn't result in a safe operation, and why the scope for name actually matters.
This will not work - if you delete an object that wasn't allocated by new you've violated the rules or the delete operator.
If you need to have your vector store objects that may or may not need to be deleted, you'll need to keep track of that somehow. One option is to use a smart pointer that keeps track of whether the pointed to object is dynamic or not. For example, shared_ptr<> allows you to specify a deallocator object when constructing the shard_ptr<> and as the docs mention:
For example, a "no-op" deallocator is useful when returning a shared_ptr to a statically allocated object
However, you should still be careful when passing pointers to automatic variables - if the vector's lifetime is longer than the lifetime of the variable then it'll be refering to garbage at some point.

C++ STL vector of vectors and memory management

We have a scenario where we need to create an
std::vector<std::vector<float>> data;
because the vectors aren't at all the same length.
When data gets freed, does every vector inside data also free up its space?
All of the standard library types implement RAII appropriately. That is, any kind of internal dynamic allocation that they perform will be cleaned up automatically by the time the object is destroyed. You never need to worry about it.
In the case of the standard containers, like std::vector, it will automatically ensure that each of its elements is destroyed. Of course, if the elements of the std::vector are themselves std::vectors, they will in turn destroy their elements. Everything is automatic.
You may have seen examples in which you have a std::vector<T*> and the T objects were then allocated manually using new. It's important to realise here that the elements of the vector are not the T objects, but T* objects. The pointers will be cleaned up automatically. Since you manually allocated the T objects, you need to manually deallocate them. (As #Veritas says in the comments, in this case you should prefer using smart pointers)
Yes
Whenever The "Scope" of 'data' ends , The destructor will be automatically called and memory that was allocated for'data' will be freed.
Whenever the destructor is called for a vector then the destructor each of its elements will be called.
Suppose for vector a(5)
Here destructors of a[0],a[1],... will be called.
Similarly in the above case vector< vector > x;
destructors of x[0],x[1] will be called consecutively..
But here each element x[i] is again a vector so again destructors of x[i][0],x[i][1]... will be called..
In this way all elements are destroyed Recursively..

fully deallocating the memory of a std::vector container

From the vector docs it would appear that the proper way to completely deallocate a vector of values to which you have a class member pointer such as:
std::vector<MyObject>* mvMyObjectVector_ptr;
...
//In the class constructor:
mvMyObjectVector_ptr = new std::vector<MyObject>();
would be to invoke the following, in order, in the class's destructor implementation
mvMyObjectVector_ptr->clear();
delete mvMyObjectVector_ptr;
However, this appears to be leading to SIGABRT 'pointer being freed was not allocated' errors. Is the above idiom the correct way to completely deallocate the memory held at the address pointed to by a pointer to a vector (if it is, I assume my errors are coming from something else)? If not, what is the correct way?
Yes, it is correct, provided mvMyObjectVector_ptr has been allocated using new.
Additionally, MyObject needs to satisfy certain requirements before it can be used with std::vector.
The call to clear() is redundant and can be omitted.
Some likely reasons for the SIGABRT include:
mvMyObjectVector_ptr hasn't been allocated using new;
MyObject violates the Rule of Three;
the class the contains the vector violates the Rule of Three.
I don' think your problem lies in the code you have shown us.
This line:
//In the class constructor:
suggests you are using this inside a class and not implementing the rule of three correctly.
A better idea is not to use a pointer to a vector.
Just declare it as a normal automatic member.
class MyClassContainingVector
{
public:
std::vector<MyObject> mvMyObjectVector;
// ^^^^^ notice no pointer.
};
Now it will be created and destroyed correctly and automatically.
Neither your constructor or destructor will need any code to manage this object.
Yes, dynamically allocating a std::vector object by calling new and invoking its destruction by calling delete will result in memory that has been internally used for holding its elements being freed.
However it would be much simpler and more reliable to follow RAII idiom and use an object with automatic storage duration:
{
std::vector<MyObject> myVector;
...
} // <-- memory is freed here
when execution leaves this scope (note that it could be also caused by exception being thrown etc.), it is guaranteed that myVector object will be destructed and memory will be freed.
Vectors usually shouldn't be held as dynamically allocated pointers. They should be just member variables of your class, in most situations (though there are always exceptions). If they are pointers allocated by 'new', and simple 'delete' should work.
Vectors handle memory allocation for you, so you don't have to. However, if you want to truly free all the storage memory held in a vector, even if it's on the stack, there's a new function for that.
Because vector.reserve() only expands memory, but never shrinks it, in C++11, a new function was added for the purpose of freeing the reserved memory: vector.shrink_to_fit(). Implementations and free to do what they want with it, but you're basically asking the implementation to free the memory, so they should answer the request in a reasonable way.
You can truly clear a vector by going like this:
vector.clear(); //Erases the elements.
vector.shrink_to_fit(); //Erases the memory no longer needed by the elements.
The reasons why vectors hold onto their memory is for performance reasons, so you should only do this if you actually understand why they have memory in reserve, and are willing to deal with (or know you won't have to deal with) any performance hits that result from micro-managing the memory.

When are objects popped from a list destroyed?

This may well be a really silly/basic question, but I can't really figure it out. I'll tell you what I think - please correct me if/where I'm wrong.
When we're using STL containers to store raw pointers, we have to take care of cleaning up after them:
std::list<animal*> alist;
animal *a = new animal();
alist.push_back(a);
...
animal *b = alist.front();
alist.pop_front();
//do stuff with b
delete b;
What happens if we store objects? It is my understanding that if a container full of objects goes out of scope, all the objects inside it are destroyed. Correct?
But what if we remove an object from a container, using std::list<T>.pop_front() for example?
std::list<animal> alist;
{
animal ani();
//ani is inserted at the end of the list (it IS ani rather than a copy
//since push_back accepts const T&)
alist.push_back(ani);
} //nothing is deallocated/destroyed here
...
{
animal b = alist.front(); //b is now a copy(?!) of the front element
//created via copy constructor
alist.pop_front(); //the front element of the list is
//destroyed/destructor is called
//do stuff with b
} //b goes out of scope
When you store something in a container, whatever you pass is copied into the container1. You still own the original object2; the container owns its copy of the object. It's true that push_back (for one example) takes its parameter by reference. This means when you pass your object to be pushed into the container, no copy is needed for use as the parameter itself, but what's put into the container is still a copy of what you told it to push. Passing by reference means only one copy is needed; if it was passed by value, (at least in C++98/03) that would have resulted in two copies: one copy from your object to the function argument, then another copy from the function argument into the container. Passing by reference allows a single copy directly from your original object directly into the container.
Likewise, when you get an object from the container, you're getting a copy of what was in the container. The container still has its object, and you have your object. Each of these has its own lifetime.
When you erase or pop an object from a container, that object is removed from the container and destroyed -- if it's something that has a destructor, the destructor will be called to destroy it.
The objects in a container will be destroyed when the container itself is destroyed. Your object will be destroyed whenever it happens to be -- the fact that it came from a container has no effect on that. If it's a local variable, it'll be destroyed when it goes out of scope. If it's a global, it'll last 'til the end of time (for the program). If you return it from a function and that's used to initialize a reference, the normal rule for extending its lifetime there will be observed. Bottom line: at that point, it's just another object, that'll have a lifetime just like any other object defined in the same way.
This can get a little...fuzzy when/if you store pointers (raw or smart) in a container. All of the above actually remains true, but the object being copied/stored is a pointer. Unless you use some container that's "aware" that you're storing pointers (e.g., Boost ptr_vector) it's just dealing with a pointer, and "ignoring" the fact that somewhere out there is an object that the pointer refers to.
In C++11, you can use, for example, emplace_back to construct an object in place in the container, but the same basic idea applies -- the container has an object that it owns, and you never touch directly.
Here I'm using "object" more like a non-programmer would -- just to mean "some thing", not necessarily an instance of a class. It may not even be an "object" in the sense that C uses the word -- it could be a pure rvalue (e.g., push_back(10)).
Yes. If you store objects and not pointers pointing to dynamic memory then you do not need to do any deallocations.
When you pop the element. The standard library takes care to call the destructor for the popped object.
The rule is:
You just need to deallocate(call delete) if you allocated something(called new).
It is my understanding that if a container full of objects goes out of scope, so do all the objects contained in it. Correct?
Firstly, some important pedantry. Objects don't go out of scope, identifiers do. This is an important distinction, which may help clarify what's going on here:
When the identifier (variable) corresponding to a container goes out of scope, the corresponding object (the container itself) is automatically deleted. Its destructor then deletes each of its elements in turn.
But what if we remove an object from a container, using std::list<T>.pop_front() for example?
The container simply deletes the contained element (as far as you're concerned, it calls its destructor). (There may be other housekeeping as well, such as shuffling all the remaining elements along by one in a std::vector, which incurs lots of destructor/copy-constructor calls).
std::list<animal> alist;
// A list is created in automatic storage (the stack). Its elements
// will exist in the free store (the heap)
{
animal ani;
// ani is created in automatic storage (the stack)
alist.push_back(ani);
// A copy of ani (using animal(const animal&) ctor) is created in
// the list "alist" via memory allocated on the free store (the heap)
} // ani.~animal() is called, then the storage for ani is recycled
// (ie, the stack space that ani used can be reused)
{
animal b = alist.front();
// either animal(animal&) or animal(animal const&)
// is called on the previous line, constructing an instance
// of animal called "b" in automatic storage (the stack)
// This is a copy of a copy of the animal instance called "ani"
// above, assuming nothing else besides this code has manipulated
// alist
alist.pop_front();
// The animal in the alist that is a copy of ani is destroyed
// from the free store (the heap)
//do stuff with b
} // b.~animal() is called, then the memory in automatic storage
// (the stack) that b lived in is recycled for other purposes
If you remove an object from a STL container, its destructor is called if it's a struct/class-type. Also depending on the container, the internal data-structure used to store the object is deallocated/destroyed as well.
Keep in mind that pointer-types are not struct/class-types and therefore do not have destructors that can manage the memory being pointed to. Therefore if you want to avoid accidental memory leaks when removing pointers from an STL container, it's best to use a smart-pointer type such as std::shared_ptr<T> that will properly manage the memory allocated to the pointer, and deallocate it when there are no more references to the allocated memory object.

Is it possible to delete a non-new object?

I have an object with a vector of pointers to other objects in it, something like this:
class Object {
...
vector<Object*> objlist;
...
};
Now, Objects will be added to list in both of these ways:
Object obj;
obj.objlist.push_back(new Object);
and
Object name;
Object* anon = &name;
obj.objlist.push_back(anon);
If a make a destructor that is simply
~Object {
for (int i = 0; i < objlist.size(); i++) {
delete objlist[i];
objlist[i] = NULL;
}
}
Will there be any adverse consequences in terms of when it tries to delete an object that was not created with new?
Yes, there will be adverse effects.
You must not delete an object that was not allocated with new. If the object was allocated on the stack, your compiler has already generated a call to its destructor at the end of its scope. This means you will call the destructor twice, with potentially very bad effects.
Besides calling the destructor twice, you will also attempt to deallocate a memory block that was never allocated. The new operator presumably puts the objects on the heap; delete expects to find the object in the same region the new operator puts them. However, your object that was not allocated with new lives on the stack. This will very probably crash your program (if it does not already crash after calling the destructor a second time).
You'll also get in deep trouble if your Object on the heap lives longer than your Object on the stack: you'll get a dangling reference to somewhere on the stack, and you'll get incorrect results/crash the next time you access it.
The general rule I see is that stuff that live on the stack can reference stuff that lives on the heap, but stuff on the heap should not reference stuff on the stack because of the very high chances that they'll outlive stack objects. And pointers to both should not be mixed together.
No, you can only delete what you newed
Object* anon = &name;
When name goes out of scope, you will have an invalid pointer in your vector.
What you're actually asking is whether it's safe to delete an object not allocated via new through the delete operator, and if so, why?
Unfortunately, this is obfuscated by some other problems in your code. As mentioned, when name goes out of scope, you're going to end up with an invalid pointer.
See zneak's answer for why your original question doesn't result in a safe operation, and why the scope for name actually matters.
This will not work - if you delete an object that wasn't allocated by new you've violated the rules or the delete operator.
If you need to have your vector store objects that may or may not need to be deleted, you'll need to keep track of that somehow. One option is to use a smart pointer that keeps track of whether the pointed to object is dynamic or not. For example, shared_ptr<> allows you to specify a deallocator object when constructing the shard_ptr<> and as the docs mention:
For example, a "no-op" deallocator is useful when returning a shared_ptr to a statically allocated object
However, you should still be careful when passing pointers to automatic variables - if the vector's lifetime is longer than the lifetime of the variable then it'll be refering to garbage at some point.