Unique_ptr vs Shared_ptr vs Weak_ptr in C++: Key Differences and Usage
unique_ptr owns a resource exclusively and deletes it when out of scope, shared_ptr allows multiple owners sharing the same resource with reference counting, and weak_ptr holds a non-owning reference to a shared_ptr to avoid circular references.Quick Comparison
Here is a quick table comparing unique_ptr, shared_ptr, and weak_ptr on key factors.
| Feature | unique_ptr | shared_ptr | weak_ptr |
|---|---|---|---|
| Ownership | Exclusive ownership | Shared ownership | Non-owning reference |
| Reference Counting | No | Yes | No (observes shared_ptr) |
| Copyable | No (only movable) | Yes | No |
| Use Case | Single owner, fast and simple | Multiple owners, shared resource | Break cycles, observe resource safely |
| Memory Overhead | Low | Higher (control block) | Minimal |
| Automatic Deletion | Yes, on destruction | Yes, when last owner destroyed | No, does not delete resource |
Key Differences
unique_ptr provides exclusive ownership of a resource. It cannot be copied but can be moved, ensuring only one pointer manages the resource at a time. This makes it lightweight and efficient for simple ownership scenarios.
shared_ptr allows multiple pointers to share ownership of the same resource. It uses reference counting internally to track how many owners exist, and deletes the resource only when the last shared_ptr is destroyed or reset. This adds some overhead but enables shared access safely.
weak_ptr is a special pointer that does not own the resource but observes a shared_ptr. It is used to avoid circular references that cause memory leaks by breaking ownership cycles. You must convert a weak_ptr to a shared_ptr to access the resource safely, checking if it still exists.
Code Comparison
#include <iostream> #include <memory> int main() { std::unique_ptr<int> ptr1 = std::make_unique<int>(42); std::cout << "unique_ptr value: " << *ptr1 << std::endl; // Transfer ownership std::unique_ptr<int> ptr2 = std::move(ptr1); if (!ptr1) { std::cout << "ptr1 is now empty after move." << std::endl; } std::cout << "ptr2 value: " << *ptr2 << std::endl; return 0; }
shared_ptr Equivalent
#include <iostream> #include <memory> int main() { std::shared_ptr<int> sptr1 = std::make_shared<int>(42); std::cout << "shared_ptr value: " << *sptr1 << std::endl; // Copy ownership std::shared_ptr<int> sptr2 = sptr1; std::cout << "shared_ptr use count: " << sptr1.use_count() << std::endl; sptr1.reset(); std::cout << "After resetting sptr1, sptr2 value: " << *sptr2 << std::endl; std::cout << "shared_ptr use count: " << sptr2.use_count() << std::endl; return 0; }
When to Use Which
Choose unique_ptr when you want simple, exclusive ownership with no sharing, such as managing a resource in a single scope or class. It is the most efficient and safest choice for unique ownership.
Choose shared_ptr when multiple parts of your program need to share ownership of a resource and you want automatic cleanup when the last owner is done. It is useful for shared data or objects with complex lifetimes.
Choose weak_ptr to hold references to shared_ptr-managed objects without affecting their lifetime, especially to break circular references that cause memory leaks. Use it when you want to observe an object safely without owning it.
Key Takeaways
unique_ptr is for exclusive ownership and is movable but not copyable.shared_ptr allows multiple owners with reference counting and automatic deletion.weak_ptr observes shared_ptr without owning, preventing circular references.unique_ptr for simple ownership, shared_ptr for shared ownership, and weak_ptr to break cycles.