In C++11, you can use a shared_ptr<>
to establish an ownership relation with an object or variable and weak_ptr<>
to safely reference that object in a non-owned way.
You can also use unique_ptr<>
to establish an ownership relation with an object or variable. But what if other, non-owning objects want to also reference that object? weak_ptr<>
isn't helpful in this case. Raw pointers are helpful but bring various downsides (e.g. they can be automatically initialized to nullptr but this is accomplished through techniques that are not consistent with the std::*_ptr<>
types).
What is the equivalent of weak_ptr<>
for non-owning references to objects owned via unique_ptr<>
?
Here's a clarifying example that resembles something in a game I'm working on.
class World
{
public:
Trebuchet* trebuchet() const { return m_trebuchet.get(); }
private:
std::unique_ptr< Trebuchet > m_trebuchet;
};
class Victim
{
public:
Victim( Trebuchet* theTrebuchet ) : m_trebuchet( theTrebuchet ) {}
~Victim()
{
delete m_trebuchet; // Duh. Oops. Dumb error. Nice if the compiler helped prevent this.
}
private:
Trebuchet* m_trebuchet; // Non-owning.
};
shared_ptr< Victim > createVictim( World& world )
{
return make_shared< Victim >( world.trebuchet() );
}
Here we use a raw pointer to maintain a non-owning relationship with an object owned via unique_ptr<>
elsewhere. But is raw the best we can do?
The hope is a type of pointer that:
std::raw_ptr<T>
._ptr<
(roughly).Thus:
int* p; // Unknown value.
std::raw_ptr< int > p; // null.
Does this type already exist in C++ now, is it proposed for the future, or is another implementation broadly available in e.g. Boost?
The "notify" behavior of shared_ptr
requires reference counting the reference count control block. shared_ptr
's reference count control block(s) use separate reference counts for this. weak_ptr
instances maintain references to this block, and weak_ptr
s themselves prevent the reference count control block from being delete
ed. The pointed-to object has its destructor called when the strong count goes to zero (which may or may not result in delete
ion of the memory where that object was stored), and the control block is delete
ed only when the weak reference count goes to zero.
unique_ptr
's tenet is that it has zero overhead over a plain pointer. Allocating and maintaining reference count control blocks (to support weak_ptr
-ish semantics) breaks that tenet. If you need behavior of that description, then you really want shared semantics, even if other references to the object are non-owning. There's still sharing going on in that case -- the sharing of the state of whether or not the object has been destroyed.
If you need a generic nonowning reference and don't need notification, use plain pointers or plain references to the item in the unique_ptr
.
EDIT:
In the case of your example, it looks like Victim
should ask for a Trebuchet&
rather than a Trebuchet*
. Then it's clear who owns the object in question.
class World
{
public:
Trebuchet& trebuchet() const { return *m_trebuchet.get(); }
private:
std::unique_ptr< Trebuchet > m_trebuchet;
};
class Victim
{
public:
Victim( Trebuchet& theTrebuchet ) : m_trebuchet( theTrebuchet ) {}
~Victim()
{
delete m_trebuchet; // Compiler error. :)
}
private:
Trebuchet& m_trebuchet; // Non-owning.
};
shared_ptr< Victim > createVictim( World& world )
{
return make_shared< Victim >( world.trebuchet() );
}