std::enable_shared_from_this
是一个模板类,用于赋予继承它的类能够从其成员函数中安全地生成该对象的 std::shared_ptr
。当你有一个由 std::shared_ptr
管理的对象,并希望在该对象的成员函数内部获得一个指向自身的 std::shared_ptr
时,std::enable_shared_from_this
就非常有用。这在异步操作、事件处理回调或任何需要延长对象生命周期以等待某些操作完成的场景中特别有价值。
值得注意的一点是shared_ptr
可以直接用指向对象的指针来构造,
但是不推荐这样,因为如果同时声明了两个这样的shared_ptr
就会造成对同一空间的重复删除。
那么我们在遇到一个希望从自身复制出 shared_ptr
的情况的时候,就可能会遇到下面的代码。
#include <memory>
#include <iostream>
class BadExample : public std::enable_shared_from_this<BadExample> {
public:
//Example1
void doSomething() {
// 错误使用:直接使用this创建一个新的std::shared_ptr
std::shared_ptr<BadExample> ptr(this);
// 做一些事情...
}
//Example2
std::shared_ptr<BadExample> get_shared_ptr() {return std::shared_ptr<BadExample>(this);}
~BadExample() {
std::cout << "BadExample destroyed\n";
}
};
int main() {
// 正确创建std::shared_ptr
std::shared_ptr<BadExample> correct_ptr = std::make_shared<BadExample>();
//Example1
// 调用成员函数,其中错误地尝试使用this初始化std::shared_ptr
correct_ptr->doSomething();
//Example2
//每次调用 get_shared_ptr() 时,都会创建一个新的std::shared_ptr实例,该实例有自己的控制块
std::shared_ptr<BadExample> correct_ptr1 = correct_ptr->get_shared_ptr();
// 当main退出时,correct_ptr被销毁,BadExample对象被删除
// 但因为doSomething中创建了一个独立的控制块,尝试再次删除对象,导致未定义行为
return 0;
}
使用裸 this
指针来初始化或赋值给一个 std::shared_ptr
会导致多个独立的 std::shared_ptr
控制块被创建,这违反了 std::shared_ptr
的设计初衷——管理对象生命周期和所有权共享。每个控制块都会尝试独立管理对象的生命周期,导致对象可能会被多次删除,进而引发未定义行为,如双重删除(double delete)错误。
Example1:
在这个错误示范中,BadExample
类的 doSomething
方法试图使用 this
指针来创建一个新的 std::shared_ptr<BadExample>
实例。这会创建一个新的控制块来管理 BadExample
对象的生命周期,与 main
函数中通过 std::make_shared
创建的 std::shared_ptr
完全独立。结果,当 main
函数结束时,两个独立的 std::shared_ptr
都会尝试销毁同一个对象,可能导致程序崩溃或其他未定义行为。
正确的做法是使用 std::enable_shared_from_this
提供的 shared_from_this()
方法,它保证返回一个与当前对象共享所有权的 std::shared_ptr
,而不会创建新的控制块:
void doSomething() {
std::shared_ptr<BadExample> ptr = shared_from_this();
// 使用ptr做一些事情...
}
Example2:
这段代码的问题在于,每次调用 get_shared_ptr()
时,都会创建一个新的 std::shared_ptr
实例,该实例有自己的控制块。如果对象最初是通过 std::make_shared
或被一个 std::shared_ptr
管理的,那么通过 this
直接创建的 std::shared_ptr
将会有一个完全独立的控制块。这意味着,原有的 std::shared_ptr
和新创建的 std::shared_ptr
不会共享引用计数,可能导致对象被提前销毁或者多次销毁。
正确的用法示范
std::shared_ptr<A> get_shared_ptr() {
// 正确:使用shared_from_this()安全地获取指向*this的std::shared_ptr
return shared_from_this();
}
如何工作
- 类继承自
std::enable_shared_from_this
。 - 当一个对象被
std::shared_ptr
管理时,std::enable_shared_from_this
内部会存储一个指向该对象的弱引用(std::weak_ptr
)。 - 可以通过调用
shared_from_this()
成员函数来获取一个指向当前对象的std::shared_ptr
。这保证了返回的std::shared_ptr
与最初管理对象的std::shared_ptr
共享所有权和控制块。
使用场景和好处
- 异步操作:在异步操作中,如果你需要保证操作完成时对象仍然存活,可以通过
shared_from_this()
获取一个std::shared_ptr
,作为异步操作的一个参数传递给回调函数或绑定到回调函数上。这样,只要异步操作尚未完成,对象就不会被销毁。 - 事件处理:在事件驱动的设计中,如果对象需要在事件发生时处理这些事件,通过
shared_from_this()
获取的std::shared_ptr
可以确保在处理事件的过程中对象保持有效。 - 避免悬挂指针:它帮助避免使用原始指针可能导致的悬挂指针问题,因为
shared_from_this()
确保只要有shared_ptr
存在,对象就不会被意外销毁。
注意事项
- 只有当至少有一个
std::shared_ptr
实例已经拥有对象的所有权时,调用shared_from_this()
才是安全的。否则,尝试调用shared_from_this()
会抛出std::bad_weak_ptr
异常。 - 因此,通常在对象的构造函数完成后,在外部通过
std::shared_ptr
管理对象时开始使用shared_from_this()
。