目录
问题背景
异步编程中对象声的声明周期需要一定的保证,通常对于一些异步的回调和lambda表达式等,均可以使用shared_ptr延长对象的声明周期,从而避免异步调用时对象指针悬空问题。但是对于一些必须在类的内部返回一个shared_ptr的情况,直接调用shared_ptr(*this)会存在double free的问题;因此需要借助enable_shared_from_this。
shared_ptr的double free问题
若直接从原生指针分别构造shared_ptr则,则会引起多个智能指针单独控制同一个对象,在指针生命周期结束时,多个智能指针触发多次对象析构,可能导致程序崩溃。
以下是一个典型的会引起double free 的代码示例:
void test_shared_ptr()
{
PveProcess*p = new PveProcess;
std::shared_ptr<PveProcess> p1(p);
//std::shared_ptr<PveProcess> p2(p);
//std::cout << p1.use_count() << std::endl;// 1
//p1 和 p2两个智能指针均从同一个原生指针p构建,有各自独立的管控单元,析构时会析构两次导致未定义行为
std::shared_ptr<PveProcess>p3(p1);
std::shared_ptr<PveProcess>p4 = p3;
std::cout << p1.use_count() << std::endl;//3 从智能指针自身拷贝构造或复值拷贝不会引起单独管控问题
}
在类的外部,只要规规矩矩的使用智能指针的拷贝构造和赋值构造,避免原生指针的乱用基本不会造成double free问题,同时可以在异步编程中延长对象的声明周期;但是在类的内部,则不尽然。
一个类的内部异步使用this问题
#include<QtConcurrent>
class ProcessPveOut
{
public:
void requestOut()
{
QtConcurrent::run(&m_threadPool, [this]() {
//一个耗时的网络处理
});
//_this 从原生指针this生成 该对象会引起double free问题
auto _this = std::shared_ptr<ProcessPveOut>(this);
QtConcurrent::run(&m_threadPool, [_this]() {
//一个耗时的网络处理
});
}
//...
private:
QThreadPool m_threadPool;
};
在使用该对象时,在超出ptr的作用域时,类内部第一种在异步表达式里面传递this指针的行为会造成指针悬空;第二种shared_ptr原本是要延长对象的声明周期,但是由于从原生指针this构造,会导致与ptr各自独立控制对象,引起double free问题。
{
//出场处理
{
auto ptr = std::shared_ptr<ProcessPveOut>(new ProcessPveOut);
ptr->requestOut(); //在ptr超出作用域时对象析构,但是requestOut可能未完成 this指针失效出问题
}
//... notify LED等后续操作
}
enable_shared_from_this
enable_shared_from_this 是个CRTP模板类,提供了一个可以返回一个shared_ptr的接口。简单来说,就是可以在类成员函数内部,返回一个共管当前对象的shared_ptr对象。以此来重写上面的业务代码即可解决以上问题。
#include<QtConcurrent>
class ProcessPveOut :public std::enable_shared_from_this<ProcessPveOut>
{
public:
void requestOut()
{
auto _this = shared_from_this();
QtConcurrent::run(&m_threadPool, [_this]() {
//一个耗时的网络处理
});
}
//...
private:
QThreadPool m_threadPool;
};
注意事项
只允许在先前已被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),这会绕过已有的引用计数管理,导致内存管理异常。


1359

被折叠的 条评论
为什么被折叠?



