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.
Related
For example, using raw pointer:
Object *obPointer = new Object(); //dynamically allocating memory, meaning we have to delete it, ourselves, later
std::unordered_map<std::string, Objects*> objContainer; //container that holds a string key and a pointer to an object type.
objContainer.emplace("A", obPointer); // placing a string and a pointer to an object into the container. simple enough.
Now, if we erase that container, it won't free the memory we allocated, the "Object" type. So we have to manually delete it, right?
delete obPointer;
objContainer.erase("A");
If we hadn't deleted the obPointer, erasing the container wouldn't be enough - we would have a memory leak.
Anyways, when it comes to shared pointers, I don't know if this works, since we don't call delete on them:
std::shared_ptr<Object> obPointer = std::make_shared<Object>(); //shared pointer
std::unordered_map<std::string, std::shared_ptr<Object>> container;
container.emplace("A", obPointer);
container.erase("A");
Did the smart pointer clean up after itself? Or it will only clean itself when it goes out of scope?
Whatever happens last
Since you have a shared pointer, it keeps track of how many copies of it exist. In your case, there is obPointer in your local scope and one object inside container (so 2 in total).
After the call to erase, the object in container gets destructed and the count goes back to 1. When obPointer goes out of scope, the count goes to 0 and the QObject instance gets deleted.
So, like Dave mentioned, I should've used an unique pointer. In my example, by erasing the container, it would not free up the memory allocated with shared_ptr. It would only delete the memory if it ran out of scope or references.
Therefore, as Dave suggested, I used an unique_pointer to created the memory, then I "passed" it to the container using "std::move," which transfers ownership TO the container's pointer. Therefore, the memory will free as soon as I erase that container. Like so:
//Container
std::unordered_map<std::string, std::unique_ptr<Object>> //container;
{//Scope just to demonstrate it works
std::unique_ptr<Object> obPointer = std::make_unique<Object>(); //unique pointer
/*Now we transfer the ownership to the container, making its life spam
rely on the scope of the CONTAINER and the container itself*/
container.emplace("A", std::move(obPointer)); //std::move very important
}//only "obPointer" will die cuz of the scope, but it's null now.
// the actual object that we created didn't delete itself. It's in the container
/*If we want to free up the memory, we should just erase the specific container
or wait for it to be out of the scope.*/
container.erase("A"); //pointer deleted and memory freed
There you go. A way to "manually" delete a smart pointer. Very useful.
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.
So there are two things that I'm not sure.
If I do something like this:
void sendToDifferentThread(SomeClass &&obj);
...
{
SomeClass object;
sendToDifferentThread(std::move(object));
}
What will happen? How can there ever only be one copy of object if it's created on the stack, since when we go out of the enclosing scope everything on stack is destroyed?
If I do something like this:
SomeClass object;
doSomethingOnSameThread(std::move(object));
What will happen if I do something in the current scope to object later? It was "moved away" to some other function, so did the current function "lose" ownership of it in some way?
In C++ when an object is constructed, memory is allocated at the same time. If the constructor runs to completion (without throwing) then the object is "alive". At some point if it is a stack object and goes out of scope, or its a heap object and you call delete, its destructor is called and that original memory is freed, and then the object is "dead". C++11 std::move / move constructors don't change any of this. What a move constructor does is give you a way and a simple syntax to "destructively" copy objects.
For instance if you move construct from a std::vector<int>, instead of reading all the ints and copying them, it will copy the pointer and the size count instead to the new vector, and set the old pointer to a nullptr and size to 0 (or possibly, allocate a (tiny) new vector of minimal size... depends on implementation). Basically when you move from something you have to leave it in a "valid", "alive" state -- it's not "dead" after you move from it, and the destructor is still going to be called later. It didn't "move" in the sense that it's still following the same lifetime and now it's just "somewhere else in memory". When you "move" from an object, there are definitely two different objects involved from C++'s point of view and I don't think you can make sense of it after a certain point if you try to think of it as though there's only one object that exists in that scenario.
I just waste hours on a simple line causing data loss. I have AnotherClass holding a vector of instances of MyClass. This AnotherClass instantiates objects of MyClass the following way:
AnotherClass::AnotherClass(){
MyClass myObject(...);
myVector.push_back(&myObject);
}
The address of myObject is afterwards pushed into a vector (with other instances of MyClass), like written in the code. When I start using instances of AnotherClass I notice the values of MyClass were lost (completely random). When I change the code to:
AnotherClass::AnotherClass(){
MyClass* myObject = new MyClass(...);
myVector.push_back(myObject);
}
I don't have data loss.
Can somebody be so kind to explain me why the second way of creating objects doesn't lead to a loss of data? (without referencing me to books of 1.000 pages)
Simple. The first version creates a local variable on the stack, which gets destroyed automatically when it goes out of scope (at the end of the function)
Your vector just contains a pointer to where the object used to be.
The second version creates an object on the heap, which will live until you eventually delete it.
The reason is RAII.
In the first snippet you declare an object in the scope of your method/constructor.
As soon this scope ends, this is the case when method is finished, your object gets out-of-scope and becomes cleaned for you (this means that its desctructor is called). Your vector now still holds pointers to your already cleaned and thus invalid objects, thats the reason why you get garbage.
In the second snippter, your object are contained on the heap. They wont become cleaned / destroyed unless you call delete myObj;. Thats the reason why they remain valid even after the method has finsihed.
You can solve this on multiple ways:
Declare your vector as std::vector<MyClass> (notice, not a pointer type)
Keep your second snippet but make sure to delete all elements of your vector once your done
Use smart pointers if you dont want to cleanup your objects by your own (e.g std::shared_ptr or std::unique_ptr)
The first way allocates a MyClass object on the stack. That object will be deallocated the moment it goes out ofscope, I.e. when the constructor has run its course.
The second way allocates the object in dynamic memory. That object will continue to exist until you call delete on it.
The second way is the way to do it but you should add a destructor to AnotherClass that iterates through the vector and deletes all objects. Otherwise your program will have a memory leak.
Beginner's question:
Class Quote {
public:
/* ..... */
virtual Quote* clone() const & {return new Quote(*this);}
virtual Quote* clone() && {return new Quote(std::move(*this));} // (***)
/* ..... */
}
Objects allocated by new are in the free-store. *this is whatever object that calls clone() and is not necessarily a dynamically-allocated object.
How does the move mechanism work if the moved-from and moved-to objects are in different memory areas? Or perhaps they're never really in different areas and I'm missing something?
From what I understand, the move constructor creates new overheads that link to the data/memory part of the moved-from object. The data itself is not moved/changed. How does this work in the above case? If it works the same way then after new runs wouldn't we have a dynamically-allocated object that sit outside free-store (wherever *this is located?) Is this somehow resolved by std::move()? I'm not exactly sure how/why std::move() work besides that it force-returns an rvalue reference to a named object making it possible to move from that object.
From what I understand, the move constructor creates new overheads
that link to the data/memory part of the moved-from object. The data
itself is not moved/changed.
Not true. A typical move constructor ((I emphasize typical because a move constructor can really do anything the class writer wants it to do) will "steal" resources which another object owns remotely (e.g. a dynamically allocated array), by reassigning handles (e.g. a pointer to a dynamically allocated array)
When I say the resource is owned remotely, I mean it is not actually a data member of the class. The handle, however, is a data member of the class. The handle refers to the resource. The moved to object and the moved from object have distinct addresses and data members. Their data is able to be moved efficiently because it is not actually part of the class, it is referred to by the handle. The handle, unlike the resource, is small, and cheap to copy. "Moving" is actually copying handles from a source object to a destination object, then nullifying the handles in the source object so that its destructor doesn't destroy the resource.
How does the move mechanism work if the moved-from and moved-to
objects are in different memory areas?
(Again I am speaking of a typical move constructor) It's irrelevant. Wherever they happen to be stored, they still have the same memory layout. The handles are modified exactly the same. The resource will be released when the destructor of the moved-to object is called (unless that object too is moved from). That means either when the object goes out of scope, if it is on the stack, or when delete is called on a pointer pointing to it, if it is on the free-store. (There are other possibilities, but those two are obviously the most common)
A move does not move everything. Builtin data types like ints and pointers, for example can't be moved, they just can be copied. Moving is pure semantics. Best example is a std::vector or a std::string. Both typically contain a pointer to some dynamically allocated memory and some other variables. Said memory semantically belongs to the vector object, meaning it has to free the memory, it may alter its contents, it has control over it and is the only one to do so. The common term is "the vector has ownership of the allocated memory".
Now, when you copy a vector, the original will keep that ownership, and the copy will have to allocate it's own memory. In contrast, when you move a vector, the move-constructed new vector is allowed to steal the allocated memory, taking over the ownership of the resource the original vector once posessed. This can be done because the original vector is about to go out of scope anyways, and it does not need that resource any more.
However, the actual data members of the moved vector are not moved themselves. Since they are pointers and maybe ints, they can only be copied. After that has been done, the pointers and ints in the original get changed in a way that semantically ends its ownership of the allocated memory.
In short: moving is a semantic thing, on the lower technical level it is mostly a copy and setting the original to zero.