例
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 管理,并且你需要在成员函数中返回一个指向 this 的 shared_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,而不会创建新控制块。
用法
- 让你的类
MyClass公有继承std::enable_shared_from_this<MyClass> - 在需要返回
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_shared或std::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;
}
但此时对象不能分配在栈上或作为成员