weak_ptr, make_shared and memory deallocation
Is my understanding correct?
Yes. If your weak_ptr
s significantly outlive the (large) object and you are tight on memory, it may be beneficial to avoid make_shared
.
However, "large" here is measured by sizeof
, and many conceptually "large" objects (for example, most standard containers, except std::array
) are quite small by that metric, because they allocate additional memory to store their contents, which will be freed as soon as the object is destroyed.
How std::shared_ptr is deallocated?
(Had to edit the answer since I have not read the question properly).
Yes, the memory itself will be around in your snippet, since you have allocated a single block for both control block and the object via make_shared
call.
std::make_shared(), std::weak_ptr and cyclic references
It is destroyed. That's one of the reason why weak_ptr
exists.
When a
is destroyed, the reference counter becomes 0, so the object is destroyed. That means the destructor of the object is called, which destroys a->parent
too.
Don't confuse destruction with deallocation. When reference counter becomes 0, or no shared_ptr
owns the object, the object is destroyed. If there is any weak_ptr
which points the control block, the memory won't be deallocated - because the object was allocated with std::make_shared
- but the object is definitely destroyed.
std::weak_ptr assignment with std::make_shared
The weak_ptr
can only be dereferenced after locking if a shared_ptr
object still exists that's pointing to the same underlying object.
In your first part
std::shared_ptr<int> shared {std::make_shared<int>(42)};
weak = shared;
std::cout << "Meaning of life: " << *weak.lock() << std::endl;
this is indeed the case. In the second part
weak = std::make_shared<int>(23);
std::cout << "Meaning of life: " << *weak.lock() << std::endl;
it is not the case, as the shared_ptr
was a temporary object.
What you've encountered here is exactly what weak_ptr
was built for - that it be valid only as long as some other shared_ptr
points to the same underlying object. That is its purpose:
std::weak_ptr is a smart pointer that holds a non-owning ("weak") reference to an object that is managed by std::shared_ptr... If the original std::shared_ptr is destroyed at this time, the object's lifetime is extended until the temporary std::shared_ptr is destroyed as well.
Do std::weak_ptrs affect when the memory allocated by std::make_shared is deallocated?
If you use make_shared
and if the implementation uses a single allocation for both the object and the reference counts, then that allocation cannot be freed until all references (both strong and weak) have been released.
However, the object will be destroyed after all strong references have been released (regardless of whether there are still weak references).
Why does shared_ptr needs to hold reference counting for weak_ptr?
The reference count controls the lifetime of the pointed-to-object. The weak count does not, but does control (or participate in control of) the lifetime of the control block.
If the reference count goes to 0
, the object is destroyed, but not necessarily deallocated. When the weak count goes to 0
(or when the reference count goes to 0
, if there are no weak_ptr
s when that happens), the control block is destroyed and deallocated, and the storage for the object is deallocated if it wasn't already.
The separation between destroying and deallocating the pointed-to-object is an implementation detail you don't need to care about, but it is caused by using make_shared
.
If you do
shared_ptr<int> myPtr(new int{10});
you allocate the storage for the int
, then pass that into the shared_ptr
constructor, which allocates storage for the control block separately. In this case, the storage for the int
can be deallocated as early as possible: as soon as the reference count hits 0
, even if there is still a weak count.
If you do
auto myPtr = make_shared<int>(10);
then make_shared
might perform an optimisation where it allocates the storage for the int
and the control block in one go. This means that the storage for the int
can't be deallocated until the storage for the control block can also be deallocated. The lifetime of the int
ends when the reference count hits 0
, but the storage for it is not deallocated until the weak count hits 0
.
Is that clear now?
std::shared_ptr, std::weak_ptr and control block
This is a tiny edge issue that gets blown way out of proportion. The only case where this is a problem is if the object is large (relative to available memory), the size is in the base size of the object (not memory that the destructor (of the object or of any of its members) can free), and a weak pointer is likely to significantly outlive the object. This is a rare combination of cases and is almost never significant.
In what sense does a weak_ptr 'own' a shared_ptr?
The control block keeps track of all the weak_ptr references as well as the shared_ptr references. After all, the weak_ptr needs to look somewhere to see if the object is still valid.
Hence, the control block cannot be de-allocated until all shared_ptr
s and all weak_ptr
s have been destroyed. If you use make_shared
the control block and the object are allocated together, which is mostly an optimization, except if the object is outlived by any weak_ptr
s.
C++11: How is object deleted if it was constructed using make_shared
With make_shared
and allocate_shared
, there's only one single reference control block that contains the object itself. It looks something like this:
struct internal_memory_type
{
unsigned char[sizeof T] buf; // make sure the object is at the top for
// efficient dereferencing!
// book keeping data
} internal_memory;
The object is constucted in-place: ::new (internal_memory.buf) T(args...)
.
Memory for the entire block is allocated with ::operator new
, or in case of allocate_shared
with the allocator's allocate()
function.
When the object is no longer needed, the destructor is called on the object itself, some thing like internal_memory.buf->~T();
. When the reference control block is no longer needed, i.e. when all the weak references have disappeared as well as all the strong ones, the reference control block as a whole is freed with ::operator delete
, or with the allocator's deallocate()
function for allocate_shared
.
What Happens to a weak_ptr when Its shared_ptr is Destroyed?
A std::shared_ptr
is created using two pieces of memory:
A resource block: This holds the pointer to the actual underlying data, e.g. 'int*'
A control block: This holds information specific to a shared_ptr, for example reference counts.
(Sometimes these are allocated in a single chunk of memory for efficiency, see std::make_shared
)
The control block also stores reference counts for weak_ptr
. It will not be deallocated until the last weak_ptr
goes out of scope (the weak pointer reference count drops to zero).
So a weak_ptr
will know that it's expired because it has access to this control block, and it can check to see what the reference count is for a shared_ptr
Related Topics
Understanding Rvalue References
How to Show Qsqlquerymodel in Qml
Set the Digits After Decimal Point
Understanding Lapack Calls in C++ with a Simple Example
Std::Optional - Construct Empty with {} or Std::Nullopt
Const Method That Modifies *This Without Const_Cast
Threadsafe Vector Class for C++
Convert Std::String to Qstring
Hide a C++ Code Block from Intellisense
Visual Studio Debugger Error: Unable to Start Program Specified File Cannot Be Found
Does Gcc Inline C++ Functions Without the 'Inline' Keyword
Opencv Gtk+2.X Error - "Unspecified Error (The Function Is Not Implemented...)"
Are Dollar-Signs Allowed in Identifiers in C++03
Extra Leading Zeros When Printing Float Using Printf