Claude
Skills
Sign in
Back

cpp-smart-pointers

Included with Lifetime
$97 forever

Use when C++ smart pointers including unique_ptr, shared_ptr, and weak_ptr for automatic memory management following RAII principles.

General

What this skill does


# C++ Smart Pointers

Smart pointers provide automatic memory management through RAII (Resource
Acquisition Is Initialization), eliminating manual `new` and `delete` calls.
They prevent memory leaks, dangling pointers, and double-free errors while
expressing ownership semantics clearly.

## RAII Principles

RAII ties resource lifetime to object lifetime, ensuring automatic cleanup
when objects go out of scope.

```cpp
#include <memory>
#include <iostream>
#include <fstream>

// RAII wrapper for file handle
class FileHandle {
    std::unique_ptr<std::FILE, decltype(&std::fclose)> file_;

public:
    FileHandle(const char* filename, const char* mode)
        : file_(std::fopen(filename, mode), &std::fclose) {
        if (!file_) {
            throw std::runtime_error("Failed to open file");
        }
    }

    std::FILE* get() { return file_.get(); }

    // No need for explicit destructor - RAII handles cleanup
};

// Traditional approach (error-prone)
void manual_memory() {
    int* ptr = new int(42);
    // If exception thrown here, memory leaks!
    delete ptr;
}

// RAII approach (safe)
void raii_memory() {
    auto ptr = std::make_unique<int>(42);
    // Automatic cleanup even if exception thrown
}

// RAII for multiple resources
void multiple_resources() {
    auto file1 = std::make_unique<FileHandle>("data.txt", "r");
    auto file2 = std::make_unique<FileHandle>("output.txt", "w");
    // Both files automatically closed in reverse order
}
```

## unique_ptr - Exclusive Ownership

`unique_ptr` represents exclusive ownership with zero runtime overhead and
move-only semantics.

```cpp
#include <memory>
#include <vector>
#include <iostream>

class Widget {
    int id_;
public:
    Widget(int id) : id_(id) {
        std::cout << "Widget " << id_ << " created\n";
    }
    ~Widget() {
        std::cout << "Widget " << id_ << " destroyed\n";
    }
    int id() const { return id_; }
};

void unique_ptr_basics() {
    // Create unique_ptr
    std::unique_ptr<Widget> w1(new Widget(1));
    auto w2 = std::make_unique<Widget>(2);  // Preferred

    // Access members
    std::cout << "Widget ID: " << w2->id() << "\n";

    // Release ownership
    Widget* raw = w2.release();
    delete raw;  // Now we're responsible

    // Reset to new object
    w1.reset(new Widget(3));  // Old widget destroyed

    // Get raw pointer (ownership retained)
    Widget* ptr = w1.get();

    // Move ownership (unique_ptr is move-only)
    std::unique_ptr<Widget> w3 = std::move(w1);
    // w1 is now nullptr

    // Cannot copy
    // std::unique_ptr<Widget> w4 = w3;  // Compiler error
}

// Factory function returning unique_ptr
std::unique_ptr<Widget> create_widget(int id) {
    return std::make_unique<Widget>(id);
}

// Container of unique_ptr
void container_example() {
    std::vector<std::unique_ptr<Widget>> widgets;
    widgets.push_back(std::make_unique<Widget>(1));
    widgets.push_back(std::make_unique<Widget>(2));

    // Move from container
    auto w = std::move(widgets[0]);
    // widgets[0] is now nullptr
}

// Custom deleter
struct FileCloser {
    void operator()(std::FILE* fp) const {
        if (fp) {
            std::cout << "Closing file\n";
            std::fclose(fp);
        }
    }
};

void custom_deleter_example() {
    std::unique_ptr<std::FILE, FileCloser> file(
        std::fopen("data.txt", "r")
    );

    // Lambda deleter
    auto deleter = [](int* p) {
        std::cout << "Deleting: " << *p << "\n";
        delete p;
    };

    std::unique_ptr<int, decltype(deleter)> ptr(new int(42), deleter);
}
```

## shared_ptr - Shared Ownership

`shared_ptr` enables shared ownership with reference counting, allowing
multiple pointers to the same object.

```cpp
#include <memory>
#include <vector>
#include <iostream>

class Resource {
    int id_;
public:
    Resource(int id) : id_(id) {
        std::cout << "Resource " << id_ << " created\n";
    }
    ~Resource() {
        std::cout << "Resource " << id_ << " destroyed\n";
    }
    int id() const { return id_; }
};

void shared_ptr_basics() {
    // Create shared_ptr
    std::shared_ptr<Resource> r1(new Resource(1));
    auto r2 = std::make_shared<Resource>(2);  // Preferred - one allocation

    // Share ownership
    std::shared_ptr<Resource> r3 = r2;
    std::cout << "Use count: " << r2.use_count() << "\n";  // 2

    // Multiple owners
    {
        std::shared_ptr<Resource> r4 = r2;
        std::cout << "Use count: " << r2.use_count() << "\n";  // 3
    }  // r4 destroyed
    std::cout << "Use count: " << r2.use_count() << "\n";  // 2

    // Check if valid
    if (r2) {
        std::cout << "r2 is valid\n";
    }

    // Reset
    r2.reset();  // Decrements reference count
    std::cout << "Use count: " << r3.use_count() << "\n";  // 1
}

// Shared ownership in data structures
class Node {
public:
    int value;
    std::shared_ptr<Node> next;

    Node(int v) : value(v), next(nullptr) {
        std::cout << "Node " << value << " created\n";
    }
    ~Node() {
        std::cout << "Node " << value << " destroyed\n";
    }
};

void linked_list_example() {
    auto head = std::make_shared<Node>(1);
    head->next = std::make_shared<Node>(2);
    head->next->next = std::make_shared<Node>(3);
    // All nodes automatically destroyed when head goes out of scope
}

// Converting between shared_ptr and unique_ptr
void pointer_conversion() {
    // unique_ptr to shared_ptr
    auto u = std::make_unique<Resource>(1);
    std::shared_ptr<Resource> s = std::move(u);
    // u is now nullptr

    // Cannot convert shared_ptr to unique_ptr (shared ownership)
}

// Aliasing constructor
struct Data {
    int x, y;
};

void aliasing_example() {
    auto data = std::make_shared<Data>();
    data->x = 10;
    data->y = 20;

    // Create shared_ptr to member, but keeps entire object alive
    std::shared_ptr<int> px(data, &data->x);
    std::cout << "Use count: " << data.use_count() << "\n";  // 2
}
```

## weak_ptr - Breaking Cycles

`weak_ptr` provides non-owning references to `shared_ptr` objects, preventing
circular reference memory leaks.

```cpp
#include <memory>
#include <iostream>

// Without weak_ptr: circular reference leak
class BadParent;

class BadChild {
public:
    std::shared_ptr<BadParent> parent;
    ~BadChild() { std::cout << "Child destroyed\n"; }
};

class BadParent {
public:
    std::shared_ptr<BadChild> child;
    ~BadParent() { std::cout << "Parent destroyed\n"; }
};

void circular_reference_leak() {
    auto parent = std::make_shared<BadParent>();
    auto child = std::make_shared<BadChild>();

    parent->child = child;
    child->parent = parent;  // Circular reference - memory leak!
}  // Neither object destroyed!

// With weak_ptr: breaks the cycle
class Parent;

class Child {
public:
    std::weak_ptr<Parent> parent;  // weak_ptr breaks cycle
    ~Child() { std::cout << "Child destroyed\n"; }
};

class Parent {
public:
    std::shared_ptr<Child> child;
    ~Parent() { std::cout << "Parent destroyed\n"; }
};

void weak_ptr_example() {
    auto parent = std::make_shared<Parent>();
    auto child = std::make_shared<Child>();

    parent->child = child;
    child->parent = parent;  // No circular reference
}  // Both objects destroyed properly

// Using weak_ptr
void weak_ptr_usage() {
    std::weak_ptr<Resource> weak;

    {
        auto shared = std::make_shared<Resource>(1);
        weak = shared;

        std::cout << "Use count: " << shared.use_count() << "\n";  // 1
        std::cout << "Weak count: " << weak.use_count() << "\n";   // 1

        // Lock to access object
        if (auto locked = weak.lock()) {
            std::cout << "Resource still alive: " << locked->id()
                      << "\n";
            std::cout << "Use count: " << locked.use_count() << "\n";  // 2
        }
    }  // shared destroyed

    // Object is gone
    if (auto locked = weak.lock()) {
        std::cout << "Resource still alive\n";
    } else {
        std::cout << "Resource destroyed\n";
 

Related in General