Smart Pointers - shared_ptr
Master shared_ptr for automatic memory management with shared ownership
Learn how to use shared_ptr for automatic memory management when multiple owners need to share the same object.
A Simple Example
#include <iostream>
#include <memory>
#include <string>
class Asset {
std::string name;
public:
explicit Asset(const std::string& n) : name{n} {
std::cout << "Asset " << name << " created\n";
}
~Asset() {
std::cout << "Asset " << name << " destroyed\n";
}
void use() const {
std::cout << "Using asset: " << name << "\n";
}
};
int main() {
std::shared_ptr<Asset> ptr1;
{
auto ptr2{std::make_shared<Asset>("Texture")};
std::cout << "Reference count: " << ptr2.use_count() << "\n";
ptr1 = ptr2; // Now two owners
std::cout << "Reference count: " << ptr2.use_count() << "\n";
ptr2->use();
} // ptr2 destroyed, but asset still alive (ptr1 owns it)
std::cout << "After inner scope\n";
std::cout << "Reference count: " << ptr1.use_count() << "\n";
ptr1->use();
return 0;
} // Asset destroyed when ptr1 goes out of scope
Breaking It Down
Reference Counting Mechanism
- Each shared_ptr maintains a reference count - how many shared_ptrs share the object
- Creating new shared_ptr to same object increments count
- Destroying shared_ptr decrements count
- When count reaches zero, object is automatically deleted
-
Remember:
use_count()returns the current reference count
Creating shared_ptr
-
Preferred:
auto ptr = std::make_shared<T>(args);- single allocation -
Alternative:
std::shared_ptr<T> ptr{new T{args}};- two allocations - Why make_shared: More efficient, exception-safe
- Remember: Never create multiple shared_ptrs from the same raw pointer
Shared Ownership
-
Copy assignment creates shared ownership:
ptr1 = ptr2; - Both pointers manage the same object
- Object lives until ALL shared_ptrs are destroyed
- Remember: Perfect for cache, observers, shared resources
Usage and Access
-
Arrow operator:
ptr->method()to access members -
Dereference:
*ptrto get reference to object -
Check validity:
if (ptr)orif (ptr != nullptr) -
Get raw pointer:
ptr.get()- use sparingly - Remember: Treat like a regular pointer with automatic cleanup
Why This Matters
- Sometimes multiple parts of your code need to access the same object, and you can't predict which part will finish with it last.
- shared_ptr uses reference counting to track how many owners exist - the object is only deleted when the last shared_ptr is destroyed.
- This enables safe shared ownership without manual tracking, preventing both memory leaks and dangling pointers.
Critical Insight
shared_ptr's control block stores the reference count on the heap, separate from the object itself. This is why std::make_shared is more efficient than shared_ptr<T>(new T) - make_shared allocates the object and control block together in one allocation instead of two. For frequently-created objects, this difference matters!
The control block also enables weak_ptr to detect when an object has been destroyed, making weak_ptr perfect for breaking circular references.
Best Practices
Always use make_shared: std::make_shared<T>(args) is more efficient and exception-safe than new.
Share by copying: Create shared ownership by copying shared_ptrs, not from raw pointers.
Avoid circular references: Use weak_ptr when objects reference each other to prevent memory leaks.
Check before dereferencing: Use if (ptr) to ensure the pointer is valid before accessing.
Common Mistakes
Creating shared_ptr from this: Don't do shared_ptr<MyClass>(this) - use shared_from_this() instead.
Circular references: Two objects with shared_ptr to each other never get deleted - use weak_ptr to break the cycle.
Mixing new and shared_ptr: Don't create multiple shared_ptrs from same raw pointer - they'll have separate reference counts.
Thread safety misconception: Reference count is thread-safe, but the object itself is not protected.
Debug Challenge
This code has a dangerous bug. Click the highlighted line to fix it:
Quick Quiz
- When is an object owned by shared_ptr deleted?
- Why use std::make_shared instead of new?
- How do you break circular references with shared_ptr?
Practice Playground
Time to try out what you just learned! Play with the example code below, experiment by making changes and running the code to deepen your understanding.
Output:
Error:
Lesson Progress
- Fix This Code
- Quick Quiz
- Practice Playground - run once