Smart Pointers in C++ - Part 4
Introduction
Welcome to Part #4 of Smart Pointers in C++!
The Parts #1, #2 and #3 are a prerquisite for Part #4 and can be found here
Let's learn more about shared_ptr
.
shared_ptr
Copy and move semantics of We had mentioned in Part 2 that unique_ptr
does not support copy semantics (copy constructor and copy assignment), it only supports move semantics (move constructor and move assignment). This is logical, as unique_ptr
does not allow multiple owners of a single resource at any given time. So when a new smart unique pointer owns a resource, the old smart pointer has to be reset (it points to nullptr
).
shared_ptr
, on the other hand, supports both copy and move semantics.
Copy and move constructor
For shared_ptr
there is a copy constructor and a move constructor defined in the class. The following code demonstrates the usage of both.
// create an int, make ptr1 the owner
std::shared_ptr<int> ptr1 (new int(10));
// make ptr2 an owner of the int created on previous line.
// now, both ptr1 and ptr2 own the int
std::shared_ptr<int> ptr2 (ptr1); // copy constructor
// create one more int, make ptr3 the owner
std::shared_ptr<int> ptr3 (new int(15));
// invalidate ptr3, ptr4 becomes the new owner
std::shared_ptr<int> ptr4 (std::move(ptr3)); // move constructor
std::cout << ptr1.use_count() << std::endl; // 2
std::cout << ptr2.use_count() << std::endl; // 2
std::cout << ptr3.use_count() << std::endl; // 0
std::cout << ptr4.use_count() << std::endl; // 1
use_count()
is a member function ofshared_ptr
that returns the number ofshared_ptr
objects that share ownership of the resource owned by this pointer (including it). If it's an emptyshared_ptr
then zero (0) is returned.
In the above example, the first int has two shared pointers owners, viz. ptr1
and ptr2
. That's the reason ptr1.use_count()
and ptr2.use_count()
both return 2
. You will notice that ptr2
is constructed using a copy constructor.
Whereas, ptr3
is invalidated when ptr4
is constructed, as we have used a move constructor. So the int with value 15
has just one owner, ptr4
. That's the reason ptr3.use_count()
returns 0
and ptr4.use_count()
returns 1
.
Copy and move assignment
shared_ptr
class also has a copy assignment and a move asignment defined for it. The following code demonstrates the usage of both.
std::shared_ptr<int> ptr1;
std::shared_ptr<int> ptr2 (new int(10));
ptr1 = ptr2; // copy assignment
// here, ptr1 and ptr2 both own the same int
ptr2 = std::make_shared<int> (20); // move assignment
std::cout << *ptr1 << std::endl; // 10
std::cout << *ptr2 << std::endl; // 20
make_shared()
is a helper function that creates, in this example, an int and returns ashared_ptr
to that int.
In the above excample, ptr1
is initially owning nothing. Then, ptr2
owns an int
with value 10
. The statement ptr1 = ptr2
is the copy assignment. ptr1
and ptr2
are both co-owning the same int
with value 10
.
Next, when the return value of make_shared
is assigned to ptr2
, the move assignment is at work. Why? That's because, std::make_shared<int> (20)
creates a dying int. So instead of making a copy of that int
and assigning the copy to ptr2
, the compiler optimizes here and uses the move assignment.
Last, we use the overloaded dereference operator *
to get the values pointed to by ptr1
and ptr2
.
Follow this link to go to Part 5