Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the difference between an empty and a null std::shared_ptr in C++?

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?

like image 638
R.M. Avatar asked Sep 18 '14 19:09

R.M.


People also ask

Is shared_ptr empty?

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).

Can a shared_ptr be null?

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 .

What is a shared_ptr in C++?

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.

Is nullptr the same as null?

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.


2 Answers

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.

like image 61
T.C. Avatar answered Oct 20 '22 06:10

T.C.


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_ptrs 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_ptrs. 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

like image 42
anton_rh Avatar answered Oct 20 '22 06:10

anton_rh