(C++)从this构造shared_ptr导致多控制块的处理

复制代码
class MyClass {
public:
    void share() {
        std::shared_ptr<MyClass> sp(this);   // 危险!
    }
};

如果对象已经被shared_pttr管理,则会创建第二个独立控制块,导致双重删除。

正确做法:继承自 std::enable_shared_from_this<MyClass>,然后使用 shared_from_this()

std::enable_shared_from_this<MyClass>

std::enable_shared_from_this<T> 是一个辅助基类模板,它允许一个被 std::shared_ptr 管理的对象安全地生成指向自身的 shared_ptr

当你有一个对象当前正被 shared_ptr 管理,并且你需要在成员函数中返回一个指向 thisshared_ptr 时,不能 直接 return std::shared_ptr<T>(this)

这样做会从同一个原始指针创建出多个独立的引用计数控制块,导致对象被重复删除(未定义行为)。

cpp 复制代码
class Bad {
public:
    std::shared_ptr<Bad> getptr() {
        return std::shared_ptr<Bad>(this);   // 危险!
    }
};

auto p1 = std::make_shared<Bad>();
auto p2 = p1->getptr();   // p1 和 p2 有不同的控制块,双重删除

enable_shared_from_this 提供了安全的机制,从已存在的 shared_ptr 控制块中获取额外的 shared_ptr,而不会创建新控制块。

用法

  1. 让你的类 MyClass 公有继承 std::enable_shared_from_this<MyClass>
  2. 在需要返回 shared_ptr 指向 this 的地方,调用 shared_from_this()
cpp 复制代码
#include <memory>
#include <iostream>

class MyClass : public std::enable_shared_from_this<MyClass> {
public:
    std::shared_ptr<MyClass> getShared() {
        return shared_from_this();   // 安全
    }

    void print() {
        std::cout << "MyClass object" << std::endl;
    }
};

int main() {
    auto sp1 = std::make_shared<MyClass>();
    auto sp2 = sp1->getShared();   // sp2 和 sp1 共享同一控制块
    std::cout << sp1.use_count() << std::endl; // 2
    return 0;
}

原理

  • enable_shared_from_this 内部保存了一个 weak_ptr<MyClass>,它指向自身的控制块。

  • 当对象第一次被 shared_ptr 管理时(例如通过 std::make_sharedstd::shared_ptr<MyClass>(new MyClass)),该 weak_ptr 会被初始化为指向该控制块。

  • 调用 shared_from_this() 相当于从那个内部的 weak_ptr 构造一个新的 shared_ptr,因此引用计数增加,但控制块唯一。

前置条件

对象必须已经被shared_ptr管理

调用 shared_from_this() 之前,必须确保 当前对象已经被至少一个 shared_ptr 所拥有,否则抛出 std::bad_weak_ptr 异常。

cpp 复制代码
MyClass obj;                     // 栈上对象,未被 shared_ptr 管理
auto sp = obj.shared_from_this(); // 抛出 std::bad_weak_ptr

使用场景

  • 异步回调:需要将当前对象自身传递给异步任务,保证对象生命周期延长至任务完成。

  • 工厂模式 :工厂方法返回 shared_ptr,并且对象内部需要返回自己的 shared_ptr

  • 观察者模式 :被观察者需要将自己的 shared_ptr 注册给观察者。

  • 对象池 :对象需要将自己的 shared_ptr 交给外部使用。

cpp 复制代码
// 异步回调示例
class AsyncTask : public std::enable_shared_from_this<AsyncTask> {
public:
    void start() {
        auto self = shared_from_this();   // 保持对象存活
        std::thread([self]() {
            std::this_thread::sleep_for(std::chrono::seconds(1));
            self->onComplete();
        }).detach();
    }
    void onComplete() { /* 处理完成 */ }
};

注意事项

  • 不要在构造函数中调用 shared_from_this() 。此时对象尚未被 shared_ptr 接管,内部的 weak_ptr 未初始化,会抛出异常。
  • 避免多次继承 enable_shared_from_this。 只能继承一次,且通常用于最派生类。多重继承时需小心虚继承。
  • 性能开销 内部存储一个 weak_ptr,会略微增大对象大小(通常一个指针大小),但开销很小。
  • std::make_shared 兼容make_shared 创建的对象可以正常使用 shared_from_this
  • 不能用于 unique_ptr 管理的对象 。因为 unique_ptr 不维护引用计数控制块,没有对应的 weak_ptr

继承但未通过 shared_ptr 管理的自动检查的示例

设计层面强制对象只能通过 shared_ptr 创建。将构造函数设为私有,并提供静态工厂方法返回 shared_ptr。这样任何对象必然被 shared_ptr 管理,无需额外检查。

cpp 复制代码
#include <memory>

class MyClass : public std::enable_shared_from_this<MyClass> {
private:
    MyClass() = default;                     // 私有构造
public:
    static std::shared_ptr<MyClass> create() {
        return std::shared_ptr<MyClass>(new MyClass());
    }

    void doSomething() {
        auto sp = shared_from_this();        // 安全,因为对象肯定在 shared_ptr 中
    }
};

int main() {
    auto obj = MyClass::create();            // 只能用 factory
    obj->doSomething();
    // MyClass obj2;                         // 错误:构造函数私有
    return 0;
}

但此时对象不能分配在栈上或作为成员

相关推荐
倒头就睡的小比特13 小时前
算法竞赛C++常用的STL
c++·算法
weilx123414 小时前
C++笔记-文件IO-<fcntl.h>
c++
Smileyqp沛沛15 小时前
前端?C++ ?较大差异基础罗列
c++·基础·前端转c++
C语言小火车15 小时前
C/C++ 为什么需要编译器?
开发语言·c++
旖旎夜光16 小时前
力控面试题 01.01: 判定字符是否唯一(位运算) —— 题解
c++·学习·算法·leetcode·力控
吞下星星的少年·-·16 小时前
C++ 萌新语法入门篇
c++·算法比赛
霍霍的袁16 小时前
【C++】map 和 set 的使用 | 从用法到底层
开发语言·c++·学习·visual studio
another heaven17 小时前
【算法/C++ MD5算法能否逆解码?原理、C++实现与同类哈希算法对比】
c++·算法·哈希算法
程序猿编码17 小时前
告别改源码适配模型:纯 C++ 可配置 LLM 推理引擎,全格式全结构兼容
c++·大模型·llm·推理引擎
此生决int17 小时前
深入理解C++系列(20)——C++11(下)
开发语言·c++