0
0
CppComparisonBeginner · 4 min read

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.

Featureunique_ptrshared_ptrweak_ptr
OwnershipExclusive ownershipShared ownershipNon-owning reference
Reference CountingNoYesNo (observes shared_ptr)
CopyableNo (only movable)YesNo
Use CaseSingle owner, fast and simpleMultiple owners, shared resourceBreak cycles, observe resource safely
Memory OverheadLowHigher (control block)Minimal
Automatic DeletionYes, on destructionYes, when last owner destroyedNo, 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

cpp
#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;
}
Output
unique_ptr value: 42 ptr1 is now empty after move. ptr2 value: 42
↔️

shared_ptr Equivalent

cpp
#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;
}
Output
shared_ptr value: 42 shared_ptr use count: 2 After resetting sptr1, sptr2 value: 42 shared_ptr use count: 1
🎯

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.
Use unique_ptr for simple ownership, shared_ptr for shared ownership, and weak_ptr to break cycles.
Choosing the right smart pointer improves memory safety and program clarity.