我是FFZero,一个10年开发经验的cpper。
C++武器库系列尝试带领大家认识开发中常用的基础知识和工具类,补充项目工具库中的底层原理,
在C++中,std::enable_shared_from_this 是一个模板类,位于 < memory> 头文件中。它允许一个std::shared_ptr 管理的对象安全地生成一个指向自身的std::shared_ptr,使其共享对象所有权。
这种能力对于需要将自身作为参数传递给回调函数或异步操作的类来说是非常有用的。
为什么需要enable_shared_from_this?
std::enable_shared_from_this 的主要作用在于提供了在类的成员函数中安全获取指向自身 std::shared_ptr 的机制。这在异步编程、事件处理等场景中尤为重要,因为它可以确保对象在这些操作完成前不会被提前销毁,从而避免了悬空指针和未定义行为的发生。
- 异步操作与回调:在进行异步I/O、定时器回调或线程池任务调度时,可能需要确保对象在异步操作期间存活,以避免对象被提前销毁。
- 防止对象过早销毁:在事件发布-订阅模式或其他需要延长对象生命周期的场景中,std::enable_shared_from_this 可以确保对象在所有订阅者处理完事件之前不会被销毁1。
- 资源管理:在异步清理或回收资源时,防止对象自身被过早销毁
如何使用enable_shared_from_this?
要使用 std::enable_shared_from_this,首先需要让类继承自 std::enable_shared_from_this,其中 T 是类本身的类型。之后,可以通过调用shared_from_this成员函数来获取指向当前对象的 std::shared_ptr。需要注意的是,只有当对象已经被std::shared_ptr管理时,才能安全地调用 shared_from_this,否则,将导致未定义行为。
下面的示例展示了如何使用 std::enable_shared_from_this 来确保对象在异步操作期间存活:
#include <iostream>
#include <memory>
#include <thread>
class AsyncWorker : public std::enable_shared_from_this<AsyncWorker> {
public:
void startAsyncOperation() {
// 假设这是一个异步操作的模拟
auto self = shared_from_this(); // 获取自身的 shared_ptr
std::thread([self] {
// 在异步线程中使用 self,确保对象不被销毁
self->doWork();
}).detach();
}
void doWork() {
// 执行实际工作
std::cout << "Async work is done." << std::endl;
}
};
class AsyncWeakWorker : public std::enable_shared_from_this<AsyncWeakWorker > {
public:
void startAsyncOperation() {
// 假设这是一个异步操作的模拟
std::weak_ptr<AsyncWeakWorker> weak_self = shared_from_this(); // 获取自身的 shared_ptr
std::thread([weak_self] {
std::this_thread::sleep_for(std::chrono::seconds(2));
auto self = weak_self.lock(); //判断对象是否已经已经销毁了
if(self) {
self->doWork();
}
else {
std::cout << "Async weak work deconstructed";
}
}).detach();
}
void doWork() {
// 执行实际工作
std::cout << "Async weak work is done." << std::endl;
}
};
int main() {
auto worker = std::make_shared<AsyncWorker>();
worker->startAsyncOperation(); // 主线程可能在此退出,但worker对象仍然存活,直到异步操作完成
auto weak_worker = std::make_shared<AsyncWeakWorker >();
weak_worker->startAsyncOperation(); //主线程可能在此退出,weak_worker对象析构,异步操作不会执行
std::this_thread::sleep_for(std::chrono::seconds(1));
return 0;
}
在这个例子中, AsyncWorker如果没有使用 shared_from_this,一旦 worker 在主线程中超出作用域被销毁,异步线程中的 this 就会变成悬空指针。
AsyncWeakWorker如果想访问对象,先通过weak_self的lock方法进行提升操作,把weak_ptr提升为shared_ptr强智能指针。提升过程中,是通过检测它所观察的强智能指针保存的AsyncWeakWorker对象的引用计数,来判定AsyncWeakWorker对象是否存活。 如果为nullptr,说明AsyncWeakWorker对象已经析构,不能再访问。
想要继续了解weak_self的使用,可以参考weak_ptr弱引用智能指针详解。
注意事项
-
只允许在先前已被std::shared_ptr 管理的对象上调用 shared_from_this 。否则调用行为未定义 (C++17 前)抛出 std::bad_weak_ptr异常。
这取决于std::enable_shared_from_this 的实现。std::enable_shared_from_this是在内部保存一个对 this 的弱引用(例如 std::weak_ptr)。当 std::shared_ptr 的构造函数检测到无歧义且可访问的 enable_shared_from_this 基类时,如果内部存储的弱引用未被生存的 std::shared_ptr 占有,就会赋值新建的 std::shared_ptr。 -
避免在构造函数或析构函数中使用 shared_from_this:在这些阶段使用 shared_from_this 可能不安全,因为对象可能尚未完全构造或已经开始析构。
-
避免使用 std::shared_ptr(this),这会绕过已有的引用计数管理,导致内存管理异常。
#include <iostream>
#include <memory>
class A{
public:
A(){
std::cout << "A::A constructor run" << std::endl;
}
~A(){
std::cout << "A::~A destructor run" << std::endl;
}
std::shared_ptr<A> GetSharedObject(){
return std::shared_ptr<A>(this);
}
};
int main() {
std::shared_ptr<A> p(new A());
std::shared_ptr<A> q = p->GetSharedObject();
std::cout << p.use_count() << std::endl;
std::cout << q.use_count() << std::endl;
return 0;
}
程序运行输出:
从程序运行输出看出,调用GetSharedObject重新创建了管理A类对象p的std::shared_ptr,导致析构函数调用了2次,同一个对象释放了两次导致崩溃。
总结
std::enable_shared_from_this 和 shared_from_this 为类的成员函数提供了一种安全获取自身 std::shared_ptr 的方法,特别适用于需要在异步操作、回调函数中延长对象生命周期的场景。合理使用它们可以有效避免内存管理问题,提高程序的健壮性