The role of weak_ptr in shared_ptr

I understand how shared_ptr works, except for the weak_ptr role. I understand it there to detect circular references when the reference count is not zero, but beyond that I don't understand exactly how this is done. What is he doing?

+3


source to share


2 answers


See also: When is std :: weak_ptr used? what and how does weak_program work? for what.

I'll give an example of how I saw it, although the example code I whipped up is a bit confusing, so bear with me:

#include <vector>
#include <memory>
#include <ostream>

int main()
{
  // Fill container with values 1-50. This container OWNS these values.
  std::vector<std::shared_ptr<int>> owning_container;
  for(int i = 1; i <= 50; ++i)
  {
    owning_container.emplace_back(std::make_shared<int>(i));
  }

  // Create a sepearate container that references all owned values that are multiples of 5.
  std::vector<std::weak_ptr<int>> referencing_container;
  for(std::shared_ptr<int> const& i : owning_container)
  {
    if((*i) % 5 == 0)
    {
      // Make weak_ptr that references shared_ptr
      referencing_container.emplace_back(i);
    }
  }

  // Go through the owned values and delete all that are multiples of 10.
  for(auto it = owning_container.begin(); it != owning_container.end();)
  {
    std::shared_ptr<int> const& i = *it;
    if(*i % 10 == 0)
    {
      it = owning_container.erase(it);
    }
    else
    {
      ++it;
    }
  }

  // Now go through the referencing container and print out all values.
  // If we were dealing with raw pointers in both containers here we would access deleted memory,
  //   since some of the the actual resources (in owning_container) that referencing_container
  //   references have been removed.
  for(std::weak_ptr<int> const& i_ref : referencing_container)
  {
    // Check if the shared resource still exists before using it (purpose of weak_ptr)
    std::shared_ptr<int> i = i_ref.lock();
    if(i)
    {
      std::cout << *i << std::endl;
    }
  }

  return 0;
}

      



Here we have a container that contains some shared resources - an int in this case - ( shared_ptr

), which should refer to another container ( weak_ptr

). The link does not own the resource, it must be able to access it if it exists . To find out if a resource is alive, we convert weak_ptr

to shared_ptr

with weak_ptr::lock()

. Those resources that still exist will have a valid shared_ptr

one returned lock()

. Those that no longer exist will return null shared_ptr

. shared_ptr

has operator bool()

which we can use to check if it is null or not before trying to use it.

A less confusing scenario might be if you were making a game where every object in the game was represented game_object

. Say you have some sort of search logic for an enemy that requires a target game_object

. Using the above, you can keep your opponent on weak_ptr<game_object>

. He doesn't own it game_object

. If something else kills its target, its target must die; do not hang in some kind of state of uncertainty that will happen if the enemy holds shared_ptr

. This way, if the opponent's target is still alive (which can be verified by blocking weak_ptr

), it can execute the search logic; otherwise he may find a new target. "Owner" game_object

can be a kind of class game_world

- it will be a containershared_ptr<game_object>

... When an enemy needs a new target, they can search this container and create it weak_ptr

from game_world

shared_ptr

.

+3


source


Weak pointers do not claim ownership of the resource, but only refer to it. Thus, they prevent you from working on the resource in any way other than re-acquiring ownership (using a method weak_ptr::lock()

). IMHO, the most common real-world situations where this behavior is desirable are circular dependencies and (less often) caching.

The circular dependencies made with shared pointers are actual memory leaks, since the mutual reference counts of pointers will never be less than 1: if nothing else belongs to A, then B still does and vice versa. A weak pointer does not "detect" this situation. It just won't give a problem, just breaking the property loop. You can still "connect the ends of the chain" by blocking the weak pointer, but none of the shared pointers you can get through the weak pointer will survive.



The problem with caching is again that the cache shouldn't usually affect the lifetime of the cached content, it's not the responsibility of the cache. But if the cache contains shared pointers, then you cannot complete the lifetime of the cached object without having to talk to the cache, which is often inconvenient.

+2


source







All Articles