1.开发场景:
假设一个类中有一个handle()方法,该方法返回一个本类类型的share_ptr指针,外面有一个本类类型的shared_ptr指针去接该指针,该如何处理?
我们通常可能会认为在handle()方法中返回一个使用this指针初始化的shared_ptr,然后外面来接handle()方法的返回值,如下所示:
class Shared{
public:
Shared(int val):m_val(val){}
int m_val;
std::shared_ptr<Shared> handle()
{
//...
return std::shared_ptr<Shared>(this);
}
};
int main()
{
std::shared_ptr<Shared> ptr1(new Shared(33));
std::shared_ptr<Shared> ptr2 = ptr1->handle();
std::cout<<ptr1.use_count()<<std::endl;
std::cout<<ptr2.use_count()<<std::endl;
}
运行上述代码会直接挂掉,提示double free,原因是在初始化ptr1指针的时候,相当于用被实例化的对象的this指针给ptr1赋值了,在调用ptr1->handle()方法时,其内部的return std::shared_ptr<Shared>(this); 时又用this指针给一个临时的shared_ptr赋值,最终就相当于ptr2也引用自this指针,相当于同一个裸指针(this)给不同的shared_ptr赋值,最终产生double free的问题。
产生double free问题的根因就是,无法通过原指针增加shared_ptr智能指针的引用计数(无法将一个原指针给多个shared_ptr赋值,因为不会增加引用计数,导致引用的空间被提前释放掉)。从打印也可以看出,两个ptr1和ptr2的引用计数都为1。
当然如果ptr1不是智能指针,是裸指针的话,是没问题的,如下:
int main()
{
Shared *ptr1(new Shared(33));
std::shared_ptr<Shared> ptr2 = ptr1->handle();
}
2.解决方案
c++标准库为了解决上述无法通过原指针增加shared_ptr引用计数的问题,提供了enable_shared_from_this<Typename>类模板。具体做法是将待操作的类声明为std::enable_shared_from_this<Typename>模板类的派生类,然后在handle()方法中return shared_from_this; 如下所示:
class Shared : public std::enable_shared_from_this<Shared>{
public:
Shared(int val):m_val(val){}
int m_val;
std::shared_ptr<Shared> handle()
{
//...
return shared_from_this();
}
};
int main()
{
std::shared_ptr<Shared> ptr1(new Shared(33));
std::shared_ptr<Shared> ptr2 = ptr1->handle();
std::cout<<ptr1.use_count()<<std::endl;
std::cout<<ptr2.use_count()<<std::endl;
}
输出结果如下:
3.原理分析与引申
其实enable_shared_from_this<>模板类内部通过一个weak_ptr指针作为中间体,weak_ptr首先引用自shared_ptr,然后在需要返回shared_ptr的函数内部,返回一个用weak_ptr初始化的shared_ptr,这样就ok了。因为使用shared_ptr给weak_ptr初始化和赋值不会改变shared_ptr的引用计数。
enable_shared_from_this<>源码:
其实我们也可以自己再类内部定义一个weak_ptr指针来测试源码中的功能,具体如下:
class Shared{
public:
Shared(int val):m_val(val){}
int m_val;
std::shared_ptr<Shared> handle()
{
//...
return std::shared_ptr<Shared>(m_weak_ptr_this);
}
std::weak_ptr<Shared> m_weak_ptr_this;
};
int main()
{
Shared *p = new Shared(33);
//裸指针p先给ptr1赋值
std::shared_ptr<Shared> ptr1(p);
//使用shared_ptr给weak_ptr赋值
ptr1->m_weak_ptr_this = ptr1;
//再操作裸指针给ptr2赋值
std::shared_ptr<Shared> ptr2 = p->handle();
std::cout<<ptr1.use_count()<<std::endl;
std::cout<<ptr2.use_count()<<std::endl;
}
输出结果:
关于shared_from_this()的详细说明可参考C++:智能指针(5)——enable_shared_from_this工作原理、源码分析_enable_shared_from_this原理分析_cocoa0409的博客-CSDN博客