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

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

相关推荐
进击的荆棘12 小时前
优选算法——哈希表
c++·算法·leetcode·哈希算法·散列表
蜡笔小马12 小时前
12.C++设计模式-模板方法模式
c++·设计模式·模板方法模式
江屿风12 小时前
【C++笔记】内存管理流食般投喂
开发语言·c++·笔记
雪度娃娃12 小时前
行为型设计模式——备忘录模式
服务器·c++·设计模式·备忘录模式
khalil102012 小时前
代码随想录算法训练营Day-55 图论06 | 108.冗余连接、109.冗余连接II
c++·算法·leetcode·图论·并查集
进击的荆棘12 小时前
优选算法——字符串
开发语言·c++·算法·leetcode·字符串
山栀shanzhi12 小时前
长连接、短连接、心跳、断线重连
开发语言·网络·c++
玖釉-12 小时前
C++ 动态规划经典题:戳气球问题详解——从区间 DP 到状态转移
c++·动态规划
洛水水13 小时前
数据库连接池详解
数据库·c++·mysql