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() );
}
There is a genuine need for a standard pointer type to act as a non-owning, inexpensive, and well-behaved counterpoint to std::unique_ptr<>
. No such pointer has been standardized yet, but a standard has been proposed and is under discussion by the C++ standards committee. The "World's Dumbest Smart Pointer", aka std::exempt_ptr<>
would have the general semantics of other modern C++ pointer classes but would hold no responsibility either for owning the pointed-to object (as shared_ptr
and unique_ptr
do) or for correctly responding to the deletion of that object (as weak_ptr
does).
Assuming that this feature is ultimately ratified by the committee, it would fully meet the need highlighted in this question. Even if it isn't ratified by the committee, the above linked document fully expresses the need and describes a complete solution.
unique_ptr
's non-owing analog is a plain C pointer. What is different - C pointer doesn't know if the pointed data is still accessible. weak_ptr
on the other hand does. But it is impossible to replace raw
pointer with a pointer knowing about the validity of data without additional overhead (and weak_ptr
does have that overhead). That implies C-style pointer is the best in terms of speed you can get as a non-owing analog for unique_ptr
.
While you can't get a "weak" pointer to a uniquely owned object for free, the concept is useful and is used in a couple systems. See Chromium's WeakPtr and QT's QPointer for implementations.
Chromium's WeakPtr is implemented intrusively by storing a shared_ptr inside the weak-referenceable object and marking it invalid when the object is destroyed. WeakPtrs then reference that ControlBlock and check whether it's valid before handing out their raw pointer. I assume QT's QPointer is implemented similarly. Because ownership isn't shared, the original object is destroyed deterministically.
However, this means that dereferencing the WeakUniquePtr
isn't thread-safe:
Thread 1:
unique_ptr<MyObject> obj(new MyObject);
thread2.send(obj->AsWeakPtr());
...
obj.reset(); // A
Thread2:
void receive(WeakUniquePtr<MyObject> weak_obj) {
if (MyObject* obj = weak_obj.get()) {
// B
obj->use();
}
}
If line A
happens to run concurrently with line B
, thread 2 will wind up using a dangling pointer. std::weak_ptr
would prevent this problem by atomically taking a shared owning reference to the object before letting thread 2 use it, but that violates the assumption above that the object is owned uniquely. That means that any use of a WeakUniquePtr
needs to be synchronized with the destruction of the real object, and the simplest way to do that is to require that they're done in a message loop on the same thread. (Note that it's still completely safe to copy the WeakUniquePtr
back and forth across threads before using it.)
One could imagine using a custom deleter in std::unique_ptr
to implement this using standard library types, but that's left as an exercise for the reader.
boost::optional<Trebuchet&>
As Billy ONeal pointed out in his answer you likely want to pass a Trebuchet&
instead of a pointer. The problem with the reference is that you cannot pass a nullptr
, boost::optional
provides a way to have the equivilent of a nullptr
. Further details on boost::optional are here: http://www.boost.org/doc/libs/1_54_0/libs/optional/doc/html/boost_optional/detailed_semantics.html
See also this question: boost::optional<T&> vs T*
Note: std::optional<T>
is on track to make it into C++14 but std::optional<T&>
is a separate proposal that is not in the current C++14 draft. Further details here: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3672.html
In the new C++ world with shared_ptr, weak_ptr, and unique_ptr you should not be storing long lived references to objects, like your trebuchet, using raw pointers or references. Instead World should have a shared_ptr to the trebuchet and Victim should store either a shared_ptr or a weak_ptr, depending on whether the trebuchet should stick around with the victim if the world goes away. Using a weak_ptr allows you to tell if the pointer is still valid (i.e. the world still exists), there is no way to do this with a raw pointer or reference.
When you use a unique_ptr you are declaring that only the World instance will own the trebuchet. Clients of the World class can use the World object's trebuchet by calling the "get" method but should not hold on to the reference or pointer returned by the method when they are done using it. Instead they should "borrow" the trebuchet every time they want to use it by calling the "get" method.
The above being said there could be instances where you want to store a reference or raw pointer for future use to avoid the overhead of the shared_ptr. But those instances are few and far between and you need to be completely sure that you won't use the pointer or reference after the World object that owns the trebuchet has gone away.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With