cpp-smart-pointers

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

C++ Smart Pointers

C++ 智能指针

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(资源获取即初始化)机制实现自动内存管理,无需手动调用
new
delete
。它们能有效避免内存泄漏、悬垂指针和重复释放错误,同时清晰表达所有权语义。

RAII Principles

RAII 原则

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
}
RAII将资源生命周期与对象生命周期绑定,确保对象超出作用域时自动清理资源。
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 - 独占所有权

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);
}
unique_ptr
表示独占所有权,运行时开销为零,且仅支持移动语义。
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 - 共享所有权

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
}
shared_ptr
支持共享所有权,通过引用计数实现多个指针指向同一对象。
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 - 打破循环引用

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";
    }

    std::cout << "Expired: " << weak.expired() << "\n";  // true
}

// Observer pattern with weak_ptr
class Observable {
    std::vector<std::weak_ptr<class Observer>> observers_;

public:
    void attach(std::shared_ptr<Observer> observer) {
        observers_.push_back(observer);
    }

    void notify() {
        // Clean up expired observers
        observers_.erase(
            std::remove_if(observers_.begin(), observers_.end(),
                [](const auto& weak) { return weak.expired(); }),
            observers_.end()
        );

        // Notify active observers
        for (auto& weak : observers_) {
            if (auto observer = weak.lock()) {
                // Notify observer
            }
        }
    }
};
weak_ptr
提供对
shared_ptr
对象的非所有权引用,可避免循环引用导致的内存泄漏。
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";
    }

    std::cout << "Expired: " << weak.expired() << "\n";  // true
}

// Observer pattern with weak_ptr
class Observable {
    std::vector<std::weak_ptr<class Observer>> observers_;

public:
    void attach(std::shared_ptr<Observer> observer) {
        observers_.push_back(observer);
    }

    void notify() {
        // Clean up expired observers
        observers_.erase(
            std::remove_if(observers_.begin(), observers_.end(),
                [](const auto& weak) { return weak.expired(); }),
            observers_.end()
        );

        // Notify active observers
        for (auto& weak : observers_) {
            if (auto observer = weak.lock()) {
                // Notify observer
            }
        }
    }
};

enable_shared_from_this

enable_shared_from_this

enable_shared_from_this
allows objects to create
shared_ptr
instances pointing to themselves safely.
cpp
#include <memory>
#include <iostream>
#include <vector>

class Task : public std::enable_shared_from_this<Task> {
    int id_;
    std::vector<std::shared_ptr<Task>> dependencies_;

public:
    Task(int id) : id_(id) {}

    void add_dependency(std::shared_ptr<Task> dep) {
        dependencies_.push_back(dep);
    }

    // Register this task as dependency of another
    void register_with(std::shared_ptr<Task> other) {
        // Get shared_ptr to this
        other->add_dependency(shared_from_this());
    }

    int id() const { return id_; }
};

void shared_from_this_example() {
    auto task1 = std::make_shared<Task>(1);
    auto task2 = std::make_shared<Task>(2);

    task1->register_with(task2);
    // task2 now has a shared_ptr to task1
}

// Callback registration
class Button : public std::enable_shared_from_this<Button> {
    using Callback = std::function<void(std::shared_ptr<Button>)>;
    Callback on_click_;

public:
    void set_on_click(Callback callback) {
        on_click_ = callback;
    }

    void click() {
        if (on_click_) {
            on_click_(shared_from_this());
        }
    }
};

void callback_example() {
    auto button = std::make_shared<Button>();

    button->set_on_click([](std::shared_ptr<Button> btn) {
        std::cout << "Button clicked!\n";
    });

    button->click();
}
enable_shared_from_this
允许对象安全地创建指向自身的
shared_ptr
实例。
cpp
#include <memory>
#include <iostream>
#include <vector>

class Task : public std::enable_shared_from_this<Task> {
    int id_;
    std::vector<std::shared_ptr<Task>> dependencies_;

public:
    Task(int id) : id_(id) {}

    void add_dependency(std::shared_ptr<Task> dep) {
        dependencies_.push_back(dep);
    }

    // Register this task as dependency of another
    void register_with(std::shared_ptr<Task> other) {
        // Get shared_ptr to this
        other->add_dependency(shared_from_this());
    }

    int id() const { return id_; }
};

void shared_from_this_example() {
    auto task1 = std::make_shared<Task>(1);
    auto task2 = std::make_shared<Task>(2);

    task1->register_with(task2);
    // task2 now has a shared_ptr to task1
}

// Callback registration
class Button : public std::enable_shared_from_this<Button> {
    using Callback = std::function<void(std::shared_ptr<Button>)>;
    Callback on_click_;

public:
    void set_on_click(Callback callback) {
        on_click_ = callback;
    }

    void click() {
        if (on_click_) {
            on_click_(shared_from_this());
        }
    }
};

void callback_example() {
    auto button = std::make_shared<Button>();

    button->set_on_click([](std::shared_ptr<Button> btn) {
        std::cout << "Button clicked!\n";
    });

    button->click();
}

Custom Deleters

自定义删除器

Custom deleters enable smart pointers to manage non-memory resources like file handles, sockets, and database connections.
cpp
#include <memory>
#include <iostream>
#include <cstdio>

// Function deleter
void close_file(std::FILE* fp) {
    if (fp) {
        std::cout << "Closing file\n";
        std::fclose(fp);
    }
}

// Lambda deleter for shared_ptr
void shared_ptr_deleter() {
    std::shared_ptr<std::FILE> file(
        std::fopen("data.txt", "r"),
        [](std::FILE* fp) {
            if (fp) {
                std::cout << "Lambda closing file\n";
                std::fclose(fp);
            }
        }
    );
}

// Array deleter for unique_ptr
void array_deleter() {
    // Built-in array support
    std::unique_ptr<int[]> arr(new int[10]);
    arr[0] = 42;  // Automatic array delete[]

    // Custom array deleter
    std::unique_ptr<int[], void(*)(int*)> custom_arr(
        new int[10],
        [](int* p) {
            std::cout << "Custom array delete\n";
            delete[] p;
        }
    );
}

// Resource wrapper with custom deleter
template<typename T>
class ResourcePool {
    std::vector<std::unique_ptr<T>> pool_;

public:
    std::shared_ptr<T> acquire() {
        if (pool_.empty()) {
            return std::shared_ptr<T>(
                new T(),
                [this](T* ptr) { this->release(ptr); }
            );
        }

        auto ptr = pool_.back().release();
        pool_.pop_back();

        return std::shared_ptr<T>(
            ptr,
            [this](T* p) { this->release(p); }
        );
    }

private:
    void release(T* ptr) {
        pool_.push_back(std::unique_ptr<T>(ptr));
    }
};
自定义删除器允许智能指针管理非内存资源,如文件句柄、套接字和数据库连接。
cpp
#include <memory>
#include <iostream>
#include <cstdio>

// Function deleter
void close_file(std::FILE* fp) {
    if (fp) {
        std::cout << "Closing file\n";
        std::fclose(fp);
    }
}

// Lambda deleter for shared_ptr
void shared_ptr_deleter() {
    std::shared_ptr<std::FILE> file(
        std::fopen("data.txt", "r"),
        [](std::FILE* fp) {
            if (fp) {
                std::cout << "Lambda closing file\n";
                std::fclose(fp);
            }
        }
    );
}

// Array deleter for unique_ptr
void array_deleter() {
    // Built-in array support
    std::unique_ptr<int[]> arr(new int[10]);
    arr[0] = 42;  // Automatic array delete[]

    // Custom array deleter
    std::unique_ptr<int[], void(*)(int*)> custom_arr(
        new int[10],
        [](int* p) {
            std::cout << "Custom array delete\n";
            delete[] p;
        }
    );
}

// Resource wrapper with custom deleter
template<typename T>
class ResourcePool {
    std::vector<std::unique_ptr<T>> pool_;

public:
    std::shared_ptr<T> acquire() {
        if (pool_.empty()) {
            return std::shared_ptr<T>(
                new T(),
                [this](T* ptr) { this->release(ptr); }
            );
        }

        auto ptr = pool_.back().release();
        pool_.pop_back();

        return std::shared_ptr<T>(
            ptr,
            [this](T* p) { this->release(p); }
        );
    }

private:
    void release(T* ptr) {
        pool_.push_back(std::unique_ptr<T>(ptr));
    }
};

Performance Considerations

性能考量

Smart pointers have different performance characteristics that influence design decisions.
cpp
#include <memory>
#include <chrono>
#include <iostream>

struct Data {
    int values[100];
};

void performance_comparison() {
    using namespace std::chrono;

    // unique_ptr: zero overhead
    {
        auto start = high_resolution_clock::now();
        for (int i = 0; i < 1000000; ++i) {
            auto ptr = std::make_unique<Data>();
        }
        auto end = high_resolution_clock::now();
        std::cout << "unique_ptr: "
                  << duration_cast<milliseconds>(end - start).count()
                  << "ms\n";
    }

    // shared_ptr: reference counting overhead
    {
        auto start = high_resolution_clock::now();
        for (int i = 0; i < 1000000; ++i) {
            auto ptr = std::make_shared<Data>();
        }
        auto end = high_resolution_clock::now();
        std::cout << "shared_ptr: "
                  << duration_cast<milliseconds>(end - start).count()
                  << "ms\n";
    }

    // shared_ptr copying: atomic operations
    {
        auto ptr = std::make_shared<Data>();
        auto start = high_resolution_clock::now();
        for (int i = 0; i < 1000000; ++i) {
            auto copy = ptr;  // Atomic increment/decrement
        }
        auto end = high_resolution_clock::now();
        std::cout << "shared_ptr copy: "
                  << duration_cast<milliseconds>(end - start).count()
                  << "ms\n";
    }
}

// make_shared vs constructor
void allocation_efficiency() {
    // One allocation (object + control block)
    auto s1 = std::make_shared<Data>();

    // Two allocations (object, then control block)
    std::shared_ptr<Data> s2(new Data());

    // Prefer make_shared for efficiency and exception safety
}
不同智能指针具有不同的性能特征,会影响设计决策。
cpp
#include <memory>
#include <chrono>
#include <iostream>

struct Data {
    int values[100];
};

void performance_comparison() {
    using namespace std::chrono;

    // unique_ptr: zero overhead
    {
        auto start = high_resolution_clock::now();
        for (int i = 0; i < 1000000; ++i) {
            auto ptr = std::make_unique<Data>();
        }
        auto end = high_resolution_clock::now();
        std::cout << "unique_ptr: "
                  << duration_cast<milliseconds>(end - start).count()
                  << "ms\n";
    }

    // shared_ptr: reference counting overhead
    {
        auto start = high_resolution_clock::now();
        for (int i = 0; i < 1000000; ++i) {
            auto ptr = std::make_shared<Data>();
        }
        auto end = high_resolution_clock::now();
        std::cout << "shared_ptr: "
                  << duration_cast<milliseconds>(end - start).count()
                  << "ms\n";
    }

    // shared_ptr copying: atomic operations
    {
        auto ptr = std::make_shared<Data>();
        auto start = high_resolution_clock::now();
        for (int i = 0; i < 1000000; ++i) {
            auto copy = ptr;  // Atomic increment/decrement
        }
        auto end = high_resolution_clock::now();
        std::cout << "shared_ptr copy: "
                  << duration_cast<milliseconds>(end - start).count()
                  << "ms\n";
    }
}

// make_shared vs constructor
void allocation_efficiency() {
    // One allocation (object + control block)
    auto s1 = std::make_shared<Data>();

    // Two allocations (object, then control block)
    std::shared_ptr<Data> s2(new Data());

    // Prefer make_shared for efficiency and exception safety
}

Thread Safety

线程安全

Smart pointers provide specific thread-safety guarantees that must be understood for concurrent programming.
cpp
#include <memory>
#include <thread>
#include <vector>
#include <iostream>

void thread_safety() {
    auto shared = std::make_shared<int>(42);

    // Reference counting is thread-safe
    std::vector<std::thread> threads;
    for (int i = 0; i < 10; ++i) {
        threads.emplace_back([shared]() {
            auto copy = shared;  // Safe: atomic increment
            std::cout << *copy << "\n";
        });
    }

    for (auto& t : threads) {
        t.join();
    }

    // Modifying pointed-to object is NOT thread-safe
    auto data = std::make_shared<int>(0);
    std::vector<std::thread> unsafe_threads;

    for (int i = 0; i < 10; ++i) {
        unsafe_threads.emplace_back([data]() {
            ++(*data);  // Race condition!
        });
    }

    for (auto& t : unsafe_threads) {
        t.join();
    }
}

// Atomic shared_ptr operations (C++20)
void atomic_shared_ptr() {
    std::shared_ptr<int> shared = std::make_shared<int>(42);

    // Thread 1
    std::thread t1([&shared]() {
        auto local = std::atomic_load(&shared);
    });

    // Thread 2
    std::thread t2([&shared]() {
        auto new_value = std::make_shared<int>(100);
        std::atomic_store(&shared, new_value);
    });

    t1.join();
    t2.join();
}
智能指针提供特定的线程安全保障,在并发编程中必须理解这些特性。
cpp
#include <memory>
#include <thread>
#include <vector>
#include <iostream>

void thread_safety() {
    auto shared = std::make_shared<int>(42);

    // Reference counting is thread-safe
    std::vector<std::thread> threads;
    for (int i = 0; i < 10; ++i) {
        threads.emplace_back([shared]() {
            auto copy = shared;  // Safe: atomic increment
            std::cout << *copy << "\n";
        });
    }

    for (auto& t : threads) {
        t.join();
    }

    // Modifying pointed-to object is NOT thread-safe
    auto data = std::make_shared<int>(0);
    std::vector<std::thread> unsafe_threads;

    for (int i = 0; i < 10; ++i) {
        unsafe_threads.emplace_back([data]() {
            ++(*data);  // Race condition!
        });
    }

    for (auto& t : unsafe_threads) {
        t.join();
    }
}

// Atomic shared_ptr operations (C++20)
void atomic_shared_ptr() {
    std::shared_ptr<int> shared = std::make_shared<int>(42);

    // Thread 1
    std::thread t1([&shared]() {
        auto local = std::atomic_load(&shared);
    });

    // Thread 2
    std::thread t2([&shared]() {
        auto new_value = std::make_shared<int>(100);
        std::atomic_store(&shared, new_value);
    });

    t1.join();
    t2.join();
}

Best Practices

最佳实践

  1. Prefer
    make_unique
    and
    make_shared
    over explicit
    new
    for exception safety and efficiency
  2. Use
    unique_ptr
    by default; only use
    shared_ptr
    when shared ownership is truly needed
  3. Use
    weak_ptr
    to break circular references in parent-child relationships
  4. Never call
    delete
    on raw pointers obtained from
    get()
  5. Pass
    unique_ptr
    by value to transfer ownership, by reference to use without transferring
  6. Pass
    shared_ptr
    by const reference to observe, by value to share ownership
  7. Use custom deleters for managing non-memory resources
  8. Avoid creating
    shared_ptr
    from raw pointers multiple times (creates multiple control blocks)
  9. Mark move operations as
    noexcept
    when implementing custom smart pointer-like types
  10. Use
    enable_shared_from_this
    when objects need to create
    shared_ptr
    to themselves
  1. 优先使用
    make_unique
    make_shared
    而非显式
    new
    ,以保证异常安全和效率
  2. 默认使用
    unique_ptr
    ;仅当确实需要共享所有权时才使用
    shared_ptr
  3. 使用
    weak_ptr
    打破父子关系中的循环引用
  4. 切勿对通过
    get()
    获取的原始指针调用
    delete
  5. 传递
    unique_ptr
    时,按值传递以转移所有权,按引用传递以使用但不转移所有权
  6. 传递
    shared_ptr
    时,按const引用传递以观察对象,按值传递以共享所有权
  7. 使用自定义删除器管理非内存资源
  8. 避免从同一原始指针创建多个
    shared_ptr
    实例(会导致多个控制块)
  9. 实现自定义智能指针类时,将移动操作标记为
    noexcept
  10. 当对象需要创建指向自身的
    shared_ptr
    时,使用
    enable_shared_from_this

Common Pitfalls

常见陷阱

  1. Creating multiple
    shared_ptr
    instances from same raw pointer, causing double-free
  2. Storing
    shared_ptr
    in containers when
    unique_ptr
    would suffice, wasting memory
  3. Forgetting to break circular references with
    weak_ptr
    , causing memory leaks
  4. Calling
    shared_from_this()
    before object is managed by
    shared_ptr
  5. Passing smart pointers by value unnecessarily, copying reference count
  6. Using
    reset()
    instead of assignment, potentially destroying objects prematurely
  7. Assuming thread-safety of pointed-to object (only control block is thread-safe)
  8. Not checking if
    weak_ptr::lock()
    succeeds before using returned
    shared_ptr
  9. Using
    unique_ptr<T>
    for arrays without
    unique_ptr<T[]>
    syntax
  10. Mixing manual memory management with smart pointers in same codebase
  1. 从同一原始指针创建多个
    shared_ptr
    实例,导致重复释放
  2. 在容器中存储
    shared_ptr
    unique_ptr
    已足够,造成内存浪费
  3. 忘记使用
    weak_ptr
    打破循环引用,导致内存泄漏
  4. 在对象被
    shared_ptr
    管理前调用
    shared_from_this()
  5. 不必要地按值传递智能指针,导致引用计数复制
  6. 使用
    reset()
    而非赋值,可能提前销毁对象
  7. 假设指向的对象是线程安全的(仅控制块是线程安全的)
  8. 使用
    weak_ptr::lock()
    返回的
    shared_ptr
    前未检查是否成功
  9. 使用
    unique_ptr<T>
    管理数组而未使用
    unique_ptr<T[]>
    语法
  10. 在同一代码库中混合手动内存管理和智能指针

When to Use Smart Pointers

何时使用智能指针

Use smart pointers when you need:
  • Automatic memory management without garbage collection
  • Clear expression of ownership semantics in your API
  • Exception-safe resource management following RAII
  • Prevention of memory leaks in complex control flow
  • Shared ownership of objects across multiple components
  • Breaking circular references with weak pointers
  • Management of non-memory resources with custom deleters
  • Modern C++ code that avoids manual
    new
    and
    delete
  • Thread-safe reference counting for concurrent access
  • Interoperability with standard library containers and algorithms
在以下场景中建议使用智能指针:
  • 无需垃圾回收即可实现自动内存管理
  • 在API中清晰表达所有权语义
  • 遵循RAII原则实现异常安全的资源管理
  • 在复杂控制流中避免内存泄漏
  • 多个组件需要共享对象所有权
  • 使用弱指针打破循环引用
  • 使用自定义删除器管理非内存资源
  • 编写现代C++代码,避免手动
    new
    delete
  • 并发访问时需要线程安全的引用计数
  • 与标准库容器和算法交互

Resources

参考资源