4

According to the first answer in this article: Explicitly deleting a shared_ptr

Is it possible to force delete a std::shared_ptr and the object it manages like below code?

do {
    ptr.reset();
} while (!ptr.unique());

ptr.reset();  // To eliminate the last reference

Technically, this should try calling std::shared_ptr::reset if the pointer has more than 1 reference count, unless it reaches to one. Any thoughts on this?

Reza Hajianpour
  • 707
  • 1
  • 10
  • 21
  • 2
    std::weak_ptr ? – KIIV Jan 25 '19 at 19:54
  • 3
    This is definitely an [XY problem](http://xyproblem.info/). What *exactly* are you trying to do that you think that you need to do this? – Nicol Bolas Jan 25 '19 at 19:58
  • @NicolBolas I'm writing a game engine where the user of the API passes a `Model` object, and the game engine holds a pointer to the object. The engine has a `delete_model` function, which should force delete this object no matter what and where it is. – Reza Hajianpour Jan 25 '19 at 20:10
  • 4
    the "shared" in `shared_ptr`refers to "shared ownership" not "shared resource". Your ownership isnt really shared but there is a single owner and others only hold pointers (aka `weak_ptr`). – 463035818_is_not_an_ai Jan 25 '19 at 20:16
  • 2
    @RezaHajianpour: Then why are you using a shared pointer, if there is one entity who holds ownership over the object? – Nicol Bolas Jan 25 '19 at 20:16
  • @NicolBolas Because I also have a `get_model` function, where I return a `shared_ptr` and I don't want to release the ownership from the engine. – Reza Hajianpour Jan 25 '19 at 20:20
  • 4
    @RezaHajianpour: Then your code is *incoherent*. You say that this "engine" should be solely responsible for destroying the object, but this `get_model` function *explicitly* shares that responsibility with others. You cannot have it both ways. Repair the contradiction in your code, and it should be fine. – Nicol Bolas Jan 25 '19 at 20:21
  • @NicolBolas Any suggestions on how I should do it? Does returning a `std::weak_ptr` in `get_model` solve the problem? – Reza Hajianpour Jan 25 '19 at 20:33
  • 6
    @RezaHajianpour why not use `std::unique_ptr` inside the engine to maintain exclusive ownership of the `Model` object, and then have `get_model()` return a raw `Model*` pointer that does not transfer/share ownership? – Remy Lebeau Jan 25 '19 at 20:41
  • @RemyLebeau Got it! Thanks! – Reza Hajianpour Jan 25 '19 at 20:45
  • I would say rather than returning a raw `Model*`, you should return a `std::unique_ptr const&`. `get` is `const` but returns a non-`const` pointer for a non-`const` type `T`. Consumers of the engine interface certainly shouldn't be trying to use any of its owned resources after the engine instance is destroyed, so the reference would remain valid throughout the lifetime of the engine instance. Returning a raw pointer removes the self-documenting nature of the owning smart pointer. – monkey0506 May 05 '19 at 05:06
  • "the reference would remain valid..." To clarify what I mean, the `std::unique_ptr` reference would remain valid, even after the owned resource itself was destroyed (e.g., the engine calling `reset`). Consumers would necessarily have to check if the resource was still there before they could use it (by definition of a non-owned resource), which is also something that can't be done if the API only exposes raw pointers. – monkey0506 May 05 '19 at 05:11

2 Answers2

17

This code doesn't make any sense.

Once you reset ptr, it doesn't manage an object anymore. If ptr was the only shared_ptr sharing ownership, then you're done. If it wasn't... well, you don't have access to all those other ones. Calling reset() on a disengaged shared_ptr is effectively a noop - there's nothing more to reset.

Imagine a simple scenario:

std::shared_ptr<int> a = std::make_shared<int>(42);
std::shared_ptr<int> b = a; // a and b are sharing ownership of an int

do {
    a.reset();
} while (!a.unique());

The only way to reset b is to reset b - this code will reset a only, it cannot possibly reach b.

Also note that unique() was deprecated in C++17 and is removed entirely in C++20. But if even if you use use_count() instead, once you do a.reset(), a.use_count() will be equal to 0 because a no longer points to an object.

Barry
  • 286,269
  • 29
  • 621
  • 977
  • Thank you. Is the reference count saved statically in the memory then? – Reza Hajianpour Jan 25 '19 at 20:12
  • @RezaHajianpour What do you mean by "saved statically"? The reference count _is_ in memory. – Barry Jan 25 '19 at 20:16
  • I mean it's not part of the object instance of std::shared_ptr? Where is it exactly saved? – Reza Hajianpour Jan 25 '19 at 20:18
  • 2
    @RezaHajianpour Each object managed by `shared_ptr`s also has a seperate control block allocated for it that each `shared_ptr` and `weak_ptr` for that object refer to. It contains the counters. – François Andrieux Jan 25 '19 at 20:23
  • 1
    @RezaHajianpour It cannot be part of the object instance. Think about how that would work - When I create a `b` above, `a`'s destruction can't free the object. – Barry Jan 25 '19 at 20:24
8

No this is not possible (or desirable). The point of a shared pointer is that if you have one you can guarantee the object it points to (if any) will not disappear from under you until (at least) you have finished with it.

Calling ptr.reset() will only reduce the reference count by 1 - being your shared pointer's reference. It will never affect other references from other shared pointers that are sharing your object.

Galik
  • 47,303
  • 4
  • 80
  • 117