The role of weak_ptr in shared_ptr
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
.
source to share
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.
source to share