欢迎光临
我们一直在努力

智能指针——使用 std::shared_ptr 管理具备共享所有权的资源

文章目录

    • 使用 std::shared_ptr 管理具备共享所有权的资源
      • 引用计数机制
      • 控制块
      • this 指针问题与 std::enable_shared_from_this
      • 自定义删除器

使用 std::shared_ptr 管理具备共享所有权的资源

std::shared_ptr 通过引用计数(reference count)机制实现共享所有权。没有特定的 shared_ptr 独占对象,所有指向同一对象的 shared_ptr 协作管理生命周期——当最后一个 shared_ptr` 不再指向该对象时,自动销毁资源。它结合了垃圾回收的便利性与析构函数的可预测性。

不支持数组

  • 没有 std::shared_ptr<T[]> 的特化版本
  • 没有 operator[]
  • 虽可通过自定义删除器 delete[] 绕过编译,但这是糟糕的设计
  • 替代方案:std::arraystd::vectorstd::string

引用计数机制

操作引用计数变化
构造函数(拷贝) 递增(+1)
析构函数 递减(−1)
拷贝赋值 sp1 = sp2 sp1 原对象计数 −1,sp2 对象计数 +1
移动构造 / 移动赋值 不变(源指针置空,所有权转移)

📌 移动操作更快:拷贝需要原子递增引用计数,移动则完全不需要修改计数。

:::color1 性能开销

  • 大小是裸指针的两倍:内部包含一个指向资源的原始指针 + 一个指向控制块的指针
  • 引用计数动态分配:控制块通常分配在堆上(用 std::make_shared 可合并分配,见 Item 21)
  • 原子操作:引用计数的增减必须是原子的,支持多线程安全,但比非原子操作慢
  • 控制块中的虚函数:确保对象被正确销毁,但仅在对象销毁时调用一次
  • :::

    控制块

    控制块包含:

    • 引用计数(强引用)
    • 弱计数(weak count,见 Item 20)
    • 自定义删除器的拷贝(如果有)
    • 自定义分配器的拷贝(如果有)
    控制块的创建规则
    场景 是否创建新控制块
    std::make_shared ✅ 总是创建
    从 std::unique_ptr / std::auto_ptr 构造 ✅ 总是创建
    从裸指针构造 ✅ 总是创建
    从 std::shared_ptr 或 std::weak_ptr 构造 ❌ 不创建(复用)

    auto pw = new Widget;
    std::shared_ptr<Widget> spw1(pw, loggingDel); // 创建控制块 #1
    std::shared_ptr<Widget> spw2(pw, loggingDel); // 创建控制块 #2 ❌
    // *pw 会被销毁两次 → double free → 未定义行为!

    // 方法一:直接传 new 的结果
    std::shared_ptr<Widget> spw1(new Widget, loggingDel);
    std::shared_ptr<Widget> spw2(spw1); // 复用 spw1 的控制块 ✅

    // 方法二:使用 std::make_shared(无自定义删除器时优先)
    auto sp = std::make_shared<Widget>();

    this 指针问题与 std::enable_shared_from_this

    std::vector<std::shared_ptr<Widget>> processedWidgets;

    //如果外部已有 shared_ptr<Widget> 指向该对象,会导致多个控制块,引发 double free!
    class Widget {
    public:
    void process() {
    processedWidgets.emplace_back(this); // ❌ 用 this 裸指针创建新的控制块!
    }
    };

    //📌 CRTP(奇异递归模板模式):派生类继承以自身为模板参数的基类模板。
    class Widget : public std::enable_shared_from_this<Widget> {
    public:
    void process() {
    processedWidgets.emplace_back(shared_from_this()); // ✅ 复用已有控制块
    }
    };

    ⚠️ 前提:调用 shared_from_this() 前,必须已存在至少一个 shared_ptr` 指向当前对象。

    class Widget : public std::enable_shared_from_this<Widget> {
    public:
    template<typename... Ts>
    static std::shared_ptr<Widget> create(Ts&&... params) {
    return std::shared_ptr<Widget>(new Widget(std::forward<Ts>(params)...));
    }
    void process() { /* 使用 shared_from_this() */ }

    private:
    Widget() = default; // 构造函数私有,强制通过工厂创建
    };

    自定义删除器

    auto loggingDel = [](Widget* pw) {
    makeLogEntry(pw);
    delete pw;
    };
    std::shared_ptr<Widget> spw(new Widget, loggingDel);

    #### 与 unique_ptr 的关键区别
    特性 std::unique_ptr std::shared_ptr
    删除器是否是类型的一部分 ✅ 是 ❌ 不是
    不同删除器的对象类型是否相同 ❌ 不同 ✅ 相同
    对象大小是否因删除器而改变 ✅ 可能变大 ❌ 始终两个指针大小
    删除器存储位置 智能指针内部 控制块中(堆上)

    📌 灵活性优势:不同删除器的 shared_ptr` 类型相同,可放入同一容器、互相赋值。

    赞(0)
    未经允许不得转载:171主机测评 » 智能指针——使用 std::shared_ptr 管理具备共享所有权的资源
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址