C++智能指针Skill cpp-smart-pointers

C++智能指针技能涉及使用unique_ptr、shared_ptr和weak_ptr进行自动内存管理,遵循RAII原则,防止内存泄漏和资源管理错误。适用于C++后端开发、系统编程和游戏开发。关键词:C++智能指针、内存管理、RAII、unique_ptr、shared_ptr、weak_ptr、编程技巧、C++开发。

后端开发 0 次安装 0 次浏览 更新于 3/25/2026

名称:cpp-smart-pointers 用户可调用:false 描述:用于在C++中使用智能指针,包括unique_ptr、shared_ptr和weak_ptr,以遵循RAII原则进行自动内存管理。 允许工具:

  • 读取
  • 写入
  • 编辑
  • 搜索
  • 全局查找
  • Bash

C++ 智能指针

智能指针通过RAII(资源获取即初始化)提供自动内存管理,消除手动调用newdelete的需要。它们防止内存泄漏、悬空指针和双重释放错误,同时清晰地表达所有权语义。

RAII 原则

RAII将资源生命周期绑定到对象生命周期,确保对象超出作用域时自动清理。

#include <memory>
#include <iostream>
#include <fstream>

// 文件句柄的RAII包装器
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("打开文件失败");
        }
    }

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

    // 无需显式析构函数 - RAII处理清理
};

// 传统方法(易出错)
void manual_memory() {
    int* ptr = new int(42);
    // 如果这里抛出异常,内存泄漏!
    delete ptr;
}

// RAII方法(安全)
void raii_memory() {
    auto ptr = std::make_unique<int>(42);
    // 即使抛出异常也自动清理
}

// 多资源的RAII
void multiple_resources() {
    auto file1 = std::make_unique<FileHandle>("data.txt", "r");
    auto file2 = std::make_unique<FileHandle>("output.txt", "w");
    // 两个文件按反向顺序自动关闭
}

unique_ptr - 独占所有权

unique_ptr表示独占所有权,零运行时开销,并仅支持移动语义。

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

class Widget {
    int id_;
public:
    Widget(int id) : id_(id) {
        std::cout << "Widget " << id_ << " 创建
";
    }
    ~Widget() {
        std::cout << "Widget " << id_ << " 销毁
";
    }
    int id() const { return id_; }
};

void unique_ptr_basics() {
    // 创建unique_ptr
    std::unique_ptr<Widget> w1(new Widget(1));
    auto w2 = std::make_unique<Widget>(2);  // 推荐

    // 访问成员
    std::cout << "Widget ID: " << w2->id() << "
";

    // 释放所有权
    Widget* raw = w2.release();
    delete raw;  // 现在需负责

    // 重置为新对象
    w1.reset(new Widget(3));  // 旧widget销毁

    // 获取原始指针(保留所有权)
    Widget* ptr = w1.get();

    // 移动所有权(unique_ptr仅可移动)
    std::unique_ptr<Widget> w3 = std::move(w1);
    // w1 现在为 nullptr

    // 不能复制
    // std::unique_ptr<Widget> w4 = w3;  // 编译错误
}

// 返回unique_ptr的工厂函数
std::unique_ptr<Widget> create_widget(int id) {
    return std::make_unique<Widget>(id);
}

// 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));

    // 从容器中移动
    auto w = std::move(widgets[0]);
    // widgets[0] 现在为 nullptr
}

// 自定义删除器
struct FileCloser {
    void operator()(std::FILE* fp) const {
        if (fp) {
            std::cout << "关闭文件
";
            std::fclose(fp);
        }
    }
};

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

    // Lambda删除器
    auto deleter = [](int* p) {
        std::cout << "删除: " << *p << "
";
        delete p;
    };

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

shared_ptr - 共享所有权

shared_ptr通过引用计数实现共享所有权,允许多个指针指向同一对象。

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

class Resource {
    int id_;
public:
    Resource(int id) : id_(id) {
        std::cout << "Resource " << id_ << " 创建
";
    }
    ~Resource() {
        std::cout << "Resource " << id_ << " 销毁
";
    }
    int id() const { return id_; }
};

void shared_ptr_basics() {
    // 创建shared_ptr
    std::shared_ptr<Resource> r1(new Resource(1));
    auto r2 = std::make_shared<Resource>(2);  // 推荐 - 单次分配

    // 共享所有权
    std::shared_ptr<Resource> r3 = r2;
    std::cout << "使用计数: " << r2.use_count() << "
";  // 2

    // 多个所有者
    {
        std::shared_ptr<Resource> r4 = r2;
        std::cout << "使用计数: " << r2.use_count() << "
";  // 3
    }  // r4 销毁
    std::cout << "使用计数: " << r2.use_count() << "
";  // 2

    // 检查是否有效
    if (r2) {
        std::cout << "r2 有效
";
    }

    // 重置
    r2.reset();  // 减少引用计数
    std::cout << "使用计数: " << r3.use_count() << "
";  // 1
}

// 数据结构中的共享所有权
class Node {
public:
    int value;
    std::shared_ptr<Node> next;

    Node(int v) : value(v), next(nullptr) {
        std::cout << "Node " << value << " 创建
";
    }
    ~Node() {
        std::cout << "Node " << value << " 销毁
";
    }
};

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);
    // 当head超出作用域时,所有节点自动销毁
}

// 在shared_ptr和unique_ptr之间转换
void pointer_conversion() {
    // unique_ptr 到 shared_ptr
    auto u = std::make_unique<Resource>(1);
    std::shared_ptr<Resource> s = std::move(u);
    // u 现在为 nullptr

    // 不能将shared_ptr转换为unique_ptr(共享所有权)
}

// 别名构造函数
struct Data {
    int x, y;
};

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

    // 创建指向成员的shared_ptr,但保持整个对象存活
    std::shared_ptr<int> px(data, &data->x);
    std::cout << "使用计数: " << data.use_count() << "
";  // 2
}

weak_ptr - 打破循环引用

weak_ptr提供对shared_ptr对象的非拥有引用,防止循环引用导致的内存泄漏。

#include <memory>
#include <iostream>

// 没有weak_ptr:循环引用泄漏
class BadParent;

class BadChild {
public:
    std::shared_ptr<BadParent> parent;
    ~BadChild() { std::cout << "子对象销毁
"; }
};

class BadParent {
public:
    std::shared_ptr<BadChild> child;
    ~BadParent() { std::cout << "父对象销毁
"; }
};

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

    parent->child = child;
    child->parent = parent;  // 循环引用 - 内存泄漏!
}  // 两个对象都未销毁!

// 使用weak_ptr:打破循环
class Parent;

class Child {
public:
    std::weak_ptr<Parent> parent;  // weak_ptr打破循环
    ~Child() { std::cout << "子对象销毁
"; }
};

class Parent {
public:
    std::shared_ptr<Child> child;
    ~Parent() { std::cout << "父对象销毁
"; }
};

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

    parent->child = child;
    child->parent = parent;  // 无循环引用
}  // 两个对象都正确销毁

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

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

        std::cout << "使用计数: " << shared.use_count() << "
";  // 1
        std::cout << "弱引用计数: " << weak.use_count() << "
";   // 1

        // 锁定以访问对象
        if (auto locked = weak.lock()) {
            std::cout << "资源仍存活: " << locked->id()
                      << "
";
            std::cout << "使用计数: " << locked.use_count() << "
";  // 2
        }
    }  // shared 销毁

    // 对象已消失
    if (auto locked = weak.lock()) {
        std::cout << "资源仍存活
";
    } else {
        std::cout << "资源已销毁
";
    }

    std::cout << "已过期: " << weak.expired() << "
";  // true
}

// 使用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() {
        // 清理已过期的观察者
        observers_.erase(
            std::remove_if(observers_.begin(), observers_.end(),
                [](const auto& weak) { return weak.expired(); }),
            observers_.end()
        );

        // 通知活跃观察者
        for (auto& weak : observers_) {
            if (auto observer = weak.lock()) {
                // 通知观察者
            }
        }
    }
};

enable_shared_from_this

enable_shared_from_this允许对象安全地创建指向自身的shared_ptr实例。

#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);
    }

    // 将此任务注册为另一个的依赖
    void register_with(std::shared_ptr<Task> other) {
        // 获取指向此对象的shared_ptr
        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 现在有一个指向task1的shared_ptr
}

// 回调注册
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->click();
}

自定义删除器

自定义删除器使智能指针能够管理非内存资源,如文件句柄、套接字和数据库连接。

#include <memory>
#include <iostream>
#include <cstdio>

// 函数删除器
void close_file(std::FILE* fp) {
    if (fp) {
        std::cout << "关闭文件
";
        std::fclose(fp);
    }
}

// shared_ptr的Lambda删除器
void shared_ptr_deleter() {
    std::shared_ptr<std::FILE> file(
        std::fopen("data.txt", "r"),
        [](std::FILE* fp) {
            if (fp) {
                std::cout << "Lambda 关闭文件
";
                std::fclose(fp);
            }
        }
    );
}

// unique_ptr的数组删除器
void array_deleter() {
    // 内置数组支持
    std::unique_ptr<int[]> arr(new int[10]);
    arr[0] = 42;  // 自动数组删除[]

    // 自定义数组删除器
    std::unique_ptr<int[], void(*)(int*)> custom_arr(
        new int[10],
        [](int* p) {
            std::cout << "自定义数组删除
";
            delete[] p;
        }
    );
}

// 带自定义删除器的资源包装器
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));
    }
};

性能考虑

智能指针有不同的性能特性,影响设计决策。

#include <memory>
#include <chrono>
#include <iostream>

struct Data {
    int values[100];
};

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

    // unique_ptr:零开销
    {
        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
";
    }

    // shared_ptr:引用计数开销
    {
        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
";
    }

    // shared_ptr复制:原子操作
    {
        auto ptr = std::make_shared<Data>();
        auto start = high_resolution_clock::now();
        for (int i = 0; i < 1000000; ++i) {
            auto copy = ptr;  // 原子递增/递减
        }
        auto end = high_resolution_clock::now();
        std::cout << "shared_ptr 复制: "
                  << duration_cast<milliseconds>(end - start).count()
                  << "ms
";
    }
}

// make_shared 与构造函数
void allocation_efficiency() {
    // 一次分配(对象 + 控制块)
    auto s1 = std::make_shared<Data>();

    // 两次分配(对象,然后控制块)
    std::shared_ptr<Data> s2(new Data());

    // 推荐使用make_shared以提高效率和异常安全
}

线程安全

智能指针提供特定的线程安全保证,需理解以进行并发编程。

#include <memory>
#include <thread>
#include <vector>
#include <iostream>

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

    // 引用计数是线程安全的
    std::vector<std::thread> threads;
    for (int i = 0; i < 10; ++i) {
        threads.emplace_back([shared]() {
            auto copy = shared;  // 安全:原子递增
            std::cout << *copy << "
";
        });
    }

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

    // 修改指向的对象不是线程安全的
    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);  // 竞态条件!
        });
    }

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

// 原子shared_ptr操作(C++20)
void atomic_shared_ptr() {
    std::shared_ptr<int> shared = std::make_shared<int>(42);

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

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

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

最佳实践

  1. 优先使用make_uniquemake_shared而非显式new,以提高异常安全和效率
  2. 默认使用unique_ptr;仅在真正需要共享所有权时使用shared_ptr
  3. 使用weak_ptr在父子关系中打破循环引用
  4. 切勿在从get()获取的原始指针上调用delete
  5. 传递unique_ptr按值以转移所有权,按引用以使用而不转移
  6. 传递shared_ptr按常量引用以观察,按值以共享所有权
  7. 使用自定义删除器管理非内存资源
  8. 避免多次从同一原始指针创建shared_ptr(创建多个控制块)
  9. 在实现自定义智能指针类时,将移动操作标记为noexcept
  10. 当对象需要创建指向自身的shared_ptr时,使用enable_shared_from_this

常见陷阱

  1. 从同一原始指针创建多个shared_ptr实例,导致双重释放
  2. unique_ptr足够时,在容器中存储shared_ptr,浪费内存
  3. 忘记使用weak_ptr打破循环引用,导致内存泄漏
  4. 在对象由shared_ptr管理之前调用shared_from_this()
  5. 不必要地按值传递智能指针,复制引用计数
  6. 使用reset()而非赋值,可能过早销毁对象
  7. 假设指向对象的线程安全(仅控制块线程安全)
  8. 在使用返回的shared_ptr前未检查weak_ptr::lock()是否成功
  9. 对数组使用unique_ptr<T>而非unique_ptr<T[]>语法
  10. 在同一代码库中混合手动内存管理和智能指针

何时使用智能指针

在以下情况下使用智能指针:

  • 无需垃圾回收的自动内存管理
  • 在API中清晰表达所有权语义
  • 遵循RAII的异常安全资源管理
  • 防止复杂控制流中的内存泄漏
  • 跨多个组件共享对象所有权
  • 使用弱指针打破循环引用
  • 用自定义删除器管理非内存资源
  • 避免手动newdelete的现代C++代码
  • 并发访问的线程安全引用计数
  • 与标准库容器和算法的互操作性

资源