Last Updated: January 3, 2026
When managing memory in C++, avoiding leaks and ensuring proper resource management can feel like walking a tightrope.
Enter shared_ptr: a smart pointer that not only simplifies memory management but also allows multiple owners for a single resource. This shared ownership pattern can be invaluable in complex applications, enabling safer interactions with dynamically allocated objects.
In this chapter, we’ll delve into shared_ptr, covering its benefits, functionality, and practical usage.
At its core, shared_ptr is part of the C++ Standard Library's smart pointer family. It allows multiple pointers to own the same resource. When the last shared_ptr to a resource is destroyed or reset, the resource is automatically freed.
shared_ptr maintains a reference count, tracking how many shared_ptr instances point to the same object. When you create a new shared_ptr from another, the reference count increases.shared_ptr can be reset to null safely.Here’s how you can create and use a shared_ptr:
In this example, the Resource object is created and owned by res1. When res2 is created, the reference count increases. When res2 goes out of scope, it decrements the count, but the resource remains alive until res1 goes out of scope.
Creating a shared_ptr can be done in several ways. It’s essential to understand the differences to avoid potential pitfalls.
The recommended way to create a shared_ptr is by using std::make_shared. This approach is not only cleaner but also more efficient:
Using make_shared has several benefits:
You can also create a shared_ptr by passing a raw pointer, but this is less safe:
Avoid using raw pointers directly unless you're sure of the ownership semantics. This approach can lead to memory leaks if not handled correctly.
When you copy a shared_ptr, the reference count increases. Here’s how it works:
You can also transfer ownership with std::move, which transfers the control of the resource without increasing the reference count:
In this case, res3 takes over the ownership, and res1 no longer points to the resource.
Understanding how shared_ptr manages lifetimes is crucial for avoiding dangling pointers and leaks.
One of the most notorious pitfalls with shared_ptr is creating circular references. If two objects hold shared_ptrs to each other, their reference counts may never reach zero, leading to memory leaks.
Consider this example:
In this case, when createCycle exits, neither node1 nor node2 will be destructed because they are holding references to each other. To avoid this, use weak_ptr for one of the pointers, which we’ll cover in the next chapter.
Sometimes, you might need to customize the deletion of the managed object. You can do this by providing a custom deleter:
This approach is useful when you need to perform additional cleanup or manage resources in a specific way.
shared_ptr shines in scenarios where resource sharing is necessary, such as in data structures or applications with complex ownership patterns.
In a tree structure where nodes might have multiple parents, shared_ptr is an ideal choice:
In this tree example, we can easily create nodes that are shared among various parts of the tree without worrying about manual memory management.
In GUI applications, shared resources (like images or configuration data) can be managed using shared_ptr. This way, you can ensure that resources remain available as long as they are needed by any part of the application.
This shared ownership ensures that the image is loaded only once and can be used by different parts of your application without duplicating resources.
Now that you understand shared_ptr, you are ready to explore weak_ptr.
In the next chapter, we will look at how weak_ptr can help prevent circular references and provide a safer way to manage shared resources without increasing reference counts.