The cplusplus.com shared_ptr
page calls out a distinction between an empty std::shared_ptr
and a null shared_ptr
. The cppreference.com page doesn't explicitly call out the distinction, but uses both "empty" and comparison to nullptr
in its description of std::shared_ptr
behavior.
Is there a difference between an empty and a null shared_ptr
? Is there any use case for such mixed-behavior pointers? Does a non-empty null shared_ptr
even make sense? Would there ever be a case in normal usage (i.e. if you didn't explicitly construct one) where you could end up with an empty-but-non-null shared_ptr
?
And do any of these answers change if you're using the Boost version instead of the C++11 version?
The managed pointer is the one passed to the deleter when use count reaches zero. A shared_ptr may also own no objects, in which case it is called empty (an empty shared_ptr may have a non-null stored pointer if the aliasing constructor was used to create it).
A null shared_ptr does serve the same purpose as a raw null pointer. It might indicate the non-availability of data. However, for the most part, there is no reason for a null shared_ptr to possess a control block or a managed nullptr .
The shared_ptr type is a smart pointer in the C++ standard library that is designed for scenarios in which more than one owner might have to manage the lifetime of the object in memory.
nullptr is a new keyword introduced in C++11. nullptr is meant as a replacement to NULL . nullptr provides a typesafe pointer value representing an empty (null) pointer. The general rule of thumb that I recommend is that you should start using nullptr whenever you would have used NULL in the past.
It's a weird corner of shared_ptr
behavior. It has a constructor that allows you to make a shared_ptr
that owns something and points to something else:
template< class Y > shared_ptr( const shared_ptr<Y>& r, T *ptr );
The shared_ptr
constructed using this constructor shares ownership with r
, but points to whatever ptr
points to (i.e., calling get()
or operator->()
will return ptr
). This is handy for cases where ptr
points to a subobject (e.g., a data member) of the object owned by r
.
The page you linked calls a shared_ptr
that owns nothing empty, and a shared_ptr
that points to nothing (i.e., whose get() == nullptr
) null. (Empty is used in this sense by the standard; null isn't.) You can construct a null-but-not-empty shared_ptr
, but it won't be very useful. An empty-but-not-null shared_ptr
is essentially a non-owning pointer, which can be used to do some weird things like passing a pointer to something allocated on the stack to a function expecting a shared_ptr
(but I'd suggest punching whoever put shared_ptr
inside the API first).
boost::shared_ptr
also has this constructor, which they call the aliasing constructor.
Is there a difference between an empty and a null shared_ptr?
Empty shared_ptr
doesn't have control block and its use count considered to be 0. Copy of empty shared_ptr
is another empty shared_ptr
. They are both separate shared_ptr
s that doesn't share common control block because they don't have it. Empty shared_ptr
can be constructed with default constructor or with constructor that takes nullptr
.
Non-empty null shared_ptr
has control block that can be shared with other shared_ptr
s. Copy of non-empty null shared_ptr
is shared_ptr
that shares the same control block as original shared_ptr
so use count is not 0. It can be said that all copies of shared_ptr
share the same nullptr
. Non-empty null shared_ptr
can be constructed with null pointer of object's type (not nullptr
)
Here is example:
#include <iostream> #include <memory> int main() { std::cout << "std::shared_ptr<int> ptr1:" << std::endl; { std::shared_ptr<int> ptr1; std::cout << "\tuse count before copying ptr: " << ptr1.use_count() << std::endl; std::shared_ptr<int> ptr2 = ptr1; std::cout << "\tuse count after copying ptr: " << ptr1.use_count() << std::endl; std::cout << "\tptr1 is " << (ptr1 ? "not null" : "null") << std::endl; } std::cout << std::endl; std::cout << "std::shared_ptr<int> ptr1(nullptr):" << std::endl; { std::shared_ptr<int> ptr1(nullptr); std::cout << "\tuse count before copying ptr: " << ptr1.use_count() << std::endl; std::shared_ptr<int> ptr2 = ptr1; std::cout << "\tuse count after copying ptr: " << ptr1.use_count() << std::endl; std::cout << "\tptr1 is " << (ptr1 ? "not null" : "null") << std::endl; } std::cout << std::endl; std::cout << "std::shared_ptr<int> ptr1(static_cast<int*>(nullptr))" << std::endl; { std::shared_ptr<int> ptr1(static_cast<int*>(nullptr)); std::cout << "\tuse count before copying ptr: " << ptr1.use_count() << std::endl; std::shared_ptr<int> ptr2 = ptr1; std::cout << "\tuse count after copying ptr: " << ptr1.use_count() << std::endl; std::cout << "\tptr1 is " << (ptr1 ? "not null" : "null") << std::endl; } std::cout << std::endl; return 0; }
It outputs:
std::shared_ptr<int> ptr1: use count before copying ptr: 0 use count after copying ptr: 0 ptr1 is null std::shared_ptr<int> ptr1(nullptr): use count before copying ptr: 0 use count after copying ptr: 0 ptr1 is null std::shared_ptr<int> ptr1(static_cast<int*>(nullptr)) use count before copying ptr: 1 use count after copying ptr: 2 ptr1 is null
http://coliru.stacked-crooked.com/a/54f59730905ed2ff
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