Language
日本語
English

Caution

JavaScript is disabled in your browser.
This site uses JavaScript for features such as search.
For the best experience, please enable JavaScript before browsing this site.

  1. Home
  2. C++ Dictionary
  3. Smart Pointers Overview

Smart Pointers Overview

In C++, smart pointers replace raw pointers to prevent common bugs such as forgotten deallocation and double-free. Since C++11, the standard library <memory> provides three types: unique_ptr, shared_ptr, and weak_ptr, serving "single ownership", "shared ownership", and "circular reference breaking" respectively. This page covers an overview of all three and when to choose each.

Syntax

// ========================================
// Smart pointer basics
// ========================================

#include <memory>

// ----------------------------------------
// unique_ptr — single ownership (no copy, movable)
// ----------------------------------------
// Use make_unique to create an instance (C++14+ recommended)
std::unique_ptr<int> uptr = std::make_unique<int>(42);

// Dereference to read/write
int val = *uptr;   // 42

// Transfer ownership with move (uptr becomes nullptr)
std::unique_ptr<int> uptr2 = std::move(uptr);
// uptr is nullptr after the move

// ----------------------------------------
// shared_ptr — shared ownership (reference-counted)
// ----------------------------------------
// Use make_shared to create an instance
std::shared_ptr<int> sptr1 = std::make_shared<int>(100);

// Copying increases the reference count
std::shared_ptr<int> sptr2 = sptr1;

// Check the reference count
long count = sptr1.use_count();  // 2 (shared by sptr1 and sptr2)

// Automatically freed when all shared_ptrs go out of scope

// ----------------------------------------
// weak_ptr — weak reference (does not increase the count)
// ----------------------------------------
std::weak_ptr<int> wptr = sptr1;

// Promote to shared_ptr with lock() before using
if (std::shared_ptr<int> locked = wptr.lock()) {
    int v = *locked;  // valid — can be accessed
}
// If lock() returns nullptr, the object has already been freed

Syntax Reference

Syntax / TypeDescription
std::unique_ptr<T>Single owner manages the object. Not copyable but movable. Automatically freed when it goes out of scope.
std::make_unique<T>(args)Factory function to safely create a unique_ptr (C++14+). Preferred over new for exception safety.
std::move(uptr)Transfers unique_ptr ownership to another unique_ptr. The source pointer becomes nullptr.
std::shared_ptr<T>Reference-counted shared ownership. Automatically freed when the count reaches zero.
std::make_shared<T>(args)Factory function to safely and efficiently create a shared_ptr. Allocates the control block and the object in a single allocation.
sptr.use_count()Returns the reference count (number of shared owners). Primarily used for debugging.
std::weak_ptr<T>References the object managed by a shared_ptr without increasing the count. Used to break circular references.
wptr.lock()Promotes the weak_ptr to a shared_ptr. Returns nullptr if the object has already been freed.
wptr.expired()Returns true if the object pointed to by the weak_ptr has been freed. If true, lock() returns nullptr.

Sample Code

smart_pointers.cpp
// ========================================
// smart_pointers.cpp
// Demonstrates the use of unique_ptr,
// shared_ptr, and weak_ptr using Jujutsu
// Kaisen characters
// ========================================

#include <iostream>
#include <memory>
#include <string>
#include <vector>

// ========================================
// Sorcerer class
// ========================================
class Sorcerer {
public:
    std::string name;
    int grade;           // grade (1-4; special grade is 0)
    std::string cursedTechnique;

    Sorcerer(const std::string& n, int g, const std::string& ct)
        : name(n), grade(g), cursedTechnique(ct) {
        std::cout << "[create] " << name << " placed in memory." << std::endl;
    }

    ~Sorcerer() {
        std::cout << "[free] " << name << " freed from memory." << std::endl;
    }

    void showInfo() const {
        std::string gradeLabel = (grade == 0) ? "Special Grade" : ("Grade " + std::to_string(grade));
        std::cout << "  " << gradeLabel << " Sorcerer: " << name
                  << " / Technique: " << cursedTechnique << std::endl;
    }
};

// ========================================
// 1. unique_ptr demo
//    Manage a mission assignment with a single owner
// ========================================
void demoUniquePtr() {
    std::cout << "=== unique_ptr demo ===" << std::endl;

    std::unique_ptr<Sorcerer> itadori =
        std::make_unique<Sorcerer>("Itadori Yuji", 4, "Physical Enhancement (Black Flash)");

    itadori->showInfo();

    // Transfer ownership with move (itadori becomes nullptr)
    std::unique_ptr<Sorcerer> mission = std::move(itadori);

    std::cout << "  After move: itadori is "
              << (itadori == nullptr ? "nullptr" : "valid") << "." << std::endl;

    mission->showInfo();

    std::cout << "  unique_ptr scope ends v" << std::endl;
}

// ========================================
// 2. shared_ptr demo
//    Share Gojo Satoru's information across multiple departments
// ========================================
void demoSharedPtr() {
    std::cout << std::endl << "=== shared_ptr demo ===" << std::endl;

    std::shared_ptr<Sorcerer> gojo =
        std::make_shared<Sorcerer>("Gojo Satoru", 0, "Limitless (Blue/Red/Hollow Purple)");

    std::cout << "  ref count (just created): " << gojo.use_count() << std::endl;

    {
        std::shared_ptr<Sorcerer> educationDept = gojo;
        std::shared_ptr<Sorcerer> combatDept    = gojo;

        std::cout << "  ref count (shared): " << gojo.use_count() << std::endl;
        educationDept->showInfo();
    }

    std::cout << "  ref count (after scope): " << gojo.use_count() << std::endl;
    std::cout << "  shared_ptr scope ends v" << std::endl;
}

// ========================================
// 3. weak_ptr demo
//    Observe Fushiguro Megumi without increasing the count
// ========================================
void demoWeakPtr() {
    std::cout << std::endl << "=== weak_ptr demo ===" << std::endl;

    std::weak_ptr<Sorcerer> watcher;

    {
        std::shared_ptr<Sorcerer> fushiguro =
            std::make_shared<Sorcerer>("Fushiguro Megumi", 2, "Ten Shadows Technique");

        watcher = fushiguro;  // weak_ptr does not increase the count

        std::cout << "  while fushiguro is valid: expired = "
                  << (watcher.expired() ? "true" : "false") << std::endl;

        if (std::shared_ptr<Sorcerer> locked = watcher.lock()) {
            locked->showInfo();
        }

        std::cout << "  fushiguro scope ends v" << std::endl;
    }

    std::cout << "  after fushiguro freed: expired = "
              << (watcher.expired() ? "true" : "false") << std::endl;

    if (watcher.lock() == nullptr) {
        std::cout << "  lock() returned nullptr. Safely verified." << std::endl;
    }
}

// ========================================
// 4. Container management demo
//    Manage multiple sorcerers with vector
// ========================================
void demoContainer() {
    std::cout << std::endl << "=== Container management demo ===" << std::endl;

    std::vector<std::unique_ptr<Sorcerer>> team;

    team.push_back(std::make_unique<Sorcerer>("Kugisaki Nobara", 3, "Straw Doll Technique"));
    team.push_back(std::make_unique<Sorcerer>("Okkotsu Yuta",    1, "Rika (Copy)"));

    std::cout << "  Team members:" << std::endl;
    for (int i = 0; i < (int)team.size(); i++) {
        team[i]->showInfo();
    }

    std::cout << "  Container scope ends v" << std::endl;
}

int main() {
    demoUniquePtr();
    demoSharedPtr();
    demoWeakPtr();
    demoContainer();

    std::cout << std::endl << "=== main ends ===" << std::endl;
    return 0;
}
g++ -std=c++14 smart_pointers.cpp -o smart_pointers
./smart_pointers
=== unique_ptr demo ===
[create] Itadori Yuji placed in memory.
  Grade 4 Sorcerer: Itadori Yuji / Technique: Physical Enhancement (Black Flash)
  After move: itadori is nullptr.
  Grade 4 Sorcerer: Itadori Yuji / Technique: Physical Enhancement (Black Flash)
  unique_ptr scope ends v
[free] Itadori Yuji freed from memory.

=== shared_ptr demo ===
[create] Gojo Satoru placed in memory.
  ref count (just created): 1
  ref count (shared): 3
  Special Grade Sorcerer: Gojo Satoru / Technique: Limitless (Blue/Red/Hollow Purple)
  ref count (after scope): 1
  shared_ptr scope ends v
[free] Gojo Satoru freed from memory.

=== weak_ptr demo ===
[create] Fushiguro Megumi placed in memory.
  while fushiguro is valid: expired = false
  Grade 2 Sorcerer: Fushiguro Megumi / Technique: Ten Shadows Technique
  fushiguro scope ends v
[free] Fushiguro Megumi freed from memory.
  after fushiguro freed: expired = true
  lock() returned nullptr. Safely verified.

=== Container management demo ===
[create] Kugisaki Nobara placed in memory.
[create] Okkotsu Yuta placed in memory.
  Team members:
  Grade 3 Sorcerer: Kugisaki Nobara / Technique: Straw Doll Technique
  Grade 1 Sorcerer: Okkotsu Yuta / Technique: Rika (Copy)
  Container scope ends v
[free] Okkotsu Yuta freed from memory.
[free] Kugisaki Nobara freed from memory.

=== main ends ===

Common mistake 1: Trying to copy a unique_ptr

unique_ptr is not copyable. Attempting to copy it results in a compile error. To transfer ownership, use std::move.

// NG:
std::unique_ptr<Sorcerer> itadori =
    std::make_unique<Sorcerer>("Itadori Yuji", 4, "Physical Enhancement");
std::unique_ptr<Sorcerer> copy = itadori;  // compile error — unique_ptr is not copyable

OK: Transfer ownership with std::move. The source becomes nullptr.

// OK:
std::unique_ptr<Sorcerer> mission = std::move(itadori);  // transfers ownership
// itadori is now nullptr

Common mistake 2: Using a weak_ptr without lock()

A weak_ptr cannot be dereferenced directly. Always promote it to a shared_ptr with lock() before accessing the object. If lock() returns nullptr, the object has already been freed.

// NG:
std::weak_ptr<Sorcerer> watcher = gojo;
watcher->showInfo();  // compile error — weak_ptr cannot be dereferenced directly

OK: Promote to shared_ptr with lock() first.

// OK:
if (std::shared_ptr<Sorcerer> locked = watcher.lock()) {
    locked->showInfo();  // access only when valid
}

Overview

Smart pointers use RAII (Resource Acquisition Is Initialization) to automatically free memory in their destructors, preventing memory leaks and double-free errors that are common with raw pointers. unique_ptr guarantees at the type level that there is always exactly one owner, and ownership is explicitly transferred with std::move. shared_ptr maintains an internal reference count so multiple owners can share an object, but "circular references" — where two shared_ptr instances point to each other — cause the count to never reach zero, resulting in a memory leak. weak_ptr solves this by referencing a shared_ptr-managed object without incrementing the count; use lock() to safely access the object. As a general guideline: use unique_ptr for single ownership, shared_ptr for shared ownership, and weak_ptr to break circular references or for "observe only" scenarios. Note that make_unique is available from C++14 and make_shared from C++11.

If you find any errors or copyright issues, please .