9

When does memory deallocation occur in the code below?

#include <memory>

int main()
{
    auto p = std::make_shared<int>(5);
    
    std::weak_ptr<int> wp = p;
    
    p = nullptr;
    
    return wp.lock() == nullptr ? 0 : 1;
}

As follows from this post std::make_shared performs one heap-allocation. Does this mean that until at least one std::weak_ptr is alive the memory can't be deallocated?

Dmitriano
  • 1,878
  • 13
  • 29
  • 4
    After all shared_ptr are destructed or reset, the weak_ptr will keep the *control block* in memory "alive" until all weak pointers have destructed. Using `make_shared` will allocate the control block and space for the object in one allocation, hence weak_ptr will keep the memory for the control block and the memory for the destructed object until all weak_ptr are destructed. – Eljay Nov 30 '20 at 21:48
  • @Eljay I expected some kind of a miracle to happen, but you disappointed me... :) – Dmitriano Nov 30 '20 at 21:53
  • Alas, there are no miracles in C++. There are lots of incredibly impressive clever things, though! I've heard of attempts to incorporate *garbage collection* with C++, but I've not tried that kind of memory management miracle myself. – Eljay Nov 30 '20 at 22:10

2 Answers2

4

(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.

SergeyA
  • 61,605
  • 5
  • 78
  • 137
  • 1
    @Dmitriano I did misread the question initially, so edited my answer now. – SergeyA Nov 30 '20 at 21:52
  • 2
    Usually, the `std::make_shared` is a good thing to have both control block and space for the object. But some scenarios (like lots of long lived weak pointers, and shorter lived strong shared pointers) where the overhead of fallow control blocks is acceptable, but fallow space for the objects is not, then intentionally not using `std::make_shared` and doing the two-step approach is a reasonable alternative. – Eljay Nov 30 '20 at 22:04
  • @Eljay I am yet to see a design where `weak_ptr` makes code better and easier to reason about. Even `shared_ptr` alone is a thing which is rarely (albeit sometimes!) is approriate, but coupled with `weak_ptr` it becomes a code maintainer nightmare. – SergeyA Nov 30 '20 at 22:27
  • Why isn't this solved that `shared_ptr` makes a realloc for just the control block in such case? Is this seen as such a not worthy niche? – bloody Nov 30 '20 at 23:15
  • @bloody I do not see how realloc can be used in such scenario. All objects which hold a pointer to control block would need to somehow be notified about the change of pointer to control block, and there are no mechanisms to do so. Logistically, it would be the same thing as just allocating the new control block - existing objects would have no idea it happened. – SergeyA Nov 30 '20 at 23:32
  • Aftermath of such is obvious, I just missed that there are no means for preserving the same address / selective memory deallocation. Thanks. – bloody Nov 30 '20 at 23:58
  • I've not run into that scenario, in real life, either. I've found `shared_ptr` itself to be something I have little interest in actually using, because a shared_ptr is effectively the same as a global variable (well, unless the object it holds is genuinely `const`). Once you have federated ownership, the owning objects (plural) cannot ensure their own invariant. And then you get spooky action at a distance sorts of bugs. – Eljay Dec 01 '20 at 00:02
3

std::make_shared<T>() allocates a control block containing a constructed T instance, and then returns a std::shared_ptr that refers to that block. The T instance is destructed when no more std::shared_ptrs refer to the control block, but the control block itself is not freed until there are no more std::shared_ptrs or std::weak_ptrs referring to it. Which, in this example, is when both wp and p go out of scope when main() exits:

#include <memory>

int main()
{
    auto p = std::make_shared<int>(5);
    
    std::weak_ptr<int> wp = p;
    
    p = nullptr; // <-- the int is destroyed here
    
    return wp.lock() == nullptr ? 0 : 1;
} // <-- the control block is freed here when p and wp are destroyed
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770