What is shared_ptr
beginner
c++11
memory
Related: Unique Pointer and The Heap
In C++ std::shared_ptr
(pronounced “shared pointer”) is one of the “smart pointer” classes available in the Standard Library.
What makes std::shared_ptr
“smart” is that it always calls delete
on the pointer it holds onto on destruction as compared to regular pointers, which do not.
std::shared_ptr
implements the dereference operator (*
) and the arrow operator (->
) in order to behave like a pointer even though it’s really an object from a library.
The Standard Library also provides a helper function for creating shared pointers called std::make_shared()
.
std::make_shared()
calls new
for you, passing all parameters to the type’s constructor.
This function is useful because objects might throw exceptions during construction and if they do we still need to call delete
on the pointer we received from new
otherwise we will leak the pointer.
std::make_shared()
correctly handles these cases.
This is why it’s considered best practice to always use std::make_shared()
instead of calling new
.
Here’s an example:
#include <memory> #include <iostream> struct Person { Person(std::string name, int age) : name(std::move(name)), age(age) { } ~Person() { std::cout << name << " says bye!\n"; } std::string name; int age; }; void describe_person(const Person& p) { std::cout << p.name << " is " << p.age << "\n"; } int main() { // Best to use std::make_shared auto maria = std::make_shared<Person>("Maria", 40); // You can also pass any pointer to std::shared_ptr's constructor Person* p = new Person("Nushi", 12); std::shared_ptr<Person> nushi(p); // shared_ptr can be used just like regular pointers // because of operator overloading describe_person(*maria); describe_person(*nushi); nushi->age += 1; describe_person(*nushi); }
Maria is 40 Nushi is 12 Nushi is 13 Nushi says bye! Maria says bye!
Why might you want a shared pointer?
Sometimes your program may have large pieces of data, like images or sounds, that you would like to re-use again and again.
std::shared_ptr
can be great for sharing these large objects in your program.
shared_ptr
Is for Sharing
std::shared_ptr
shares ownership of its pointer with its copies, unlike std::unique_ptr
which uniquely owns its pointer and cannot be copied.
std::shared_ptr
does this by using reference counting.
Every copy of a shared pointer increases the reference count by 1 and every destructor decreases the reference count by 1.
When the last copy of a shared pointer is destroyed that copy’s destructor calls delete
on the owned pointer, ensuring delete
is always properly called, but only once.
Here’s an example:
#include <memory> #include <iostream> struct Person { Person(std::string name, int age) : name(std::move(name)), age(age) { } ~Person() { std::cout << name << " says bye!\n"; } std::string name; int age; }; void describe_person(const Person& p) { std::cout << p.name << " is " << p.age << "\n"; } int main() { // Best to use std::make_shared auto maria = std::make_shared<Person>("Maria", 40); { std::shared_ptr<Person> alsoMaria = maria; std::shared_ptr<Person> mariaAgain = alsoMaria; describe_person(*maria); maria->age += 1; describe_person(*alsoMaria); std::cout << maria.use_count() << " shared pointers\n"; } std::cout << maria.use_count() << " shared pointer\n"; }
Maria is 40 Maria is 41 3 shared pointers 1 shared pointer Maria says bye!
get()
Sometimes an API demands a pointer be passed as a parameter.
std::shared_ptr
provides a get()
method for just such a purpose.
get()
returns the underlying pointer, but be careful, the ownership of the pointer still belongs to the std::shared_ptr
object and its copies.
The pointer returned by get()
does not increase nor decrease the reference count.
#include <memory> #include <iostream> struct Person { Person(std::string name, int age) : name(std::move(name)), age(age) { } ~Person() { std::cout << name << " says bye!\n"; } std::string name; int age; }; void describe_person(Person* p) { std::cout << p->name << " is " << p->age << "\n"; } int main() { auto maria = std::make_shared<Person>("Maria", 40); describe_person(maria.get()); }
Maria is 40 Maria says bye!