1. 介绍
关于智能指针 shared_ptr 很有意思的一个问题。
可以用 std::shared_ptr 将裸指针封装起来,变成带引用计数的智能指针,当引用计数为0,C++会自动把裸指针指向的内存回收。
先定义一个类
class A
{
public:
A();
private:
int a_;
};
这里p是裸指针,sp是智能指针,管理p:
int *p = new A();
std::shared_ptr<int> sp(p); // sp的引用计数是1
现在新建个sp2,可以跟sp一起管理p了
int *p = new A();
std::shared_ptr<int> sp(p);
std::shared_ptr<int> sp2(sp); // sp 和 sp2的 引用计数都是2了
有个问题,如果再用sp2直接去管理p,会出现什么行为?
int *p = new A();
std::shared_ptr<int> sp(p); // sp引用计数是1
std::shared_ptr<int> sp2(p); // sp2的引用计数还是1
因为 sp 和 sp2 是两个不相关的变量,不知道对方也在管理着p,在各自析构的时候,会导致重复释放p。相当于free(p)了两次,程序会挂掉。
这里的问题是:如何让p的所有权,由1组相关的shared_ptr来管理,这些shared_ptr使用引用计数,相互知道对方;而不是由两组shared_ptr来管理,各自维护一套引用计数。
那么如何让sp2知道之前有个sp在管理p呢?一种方法是用sp去构造sp2,还有一种方法是通过shared_from_this
。
// 1. A继承std::enable_shared_from_this<A>模板类
class A: std::enable_shared_from_this<A>
{
public:
A();
std::shared_ptr<A> get_sp(){
// 2. 继承后,会自动获得 shared_from_this() 函数,然后调用该函数
return shared_from_this();
}
private:
int a_;
};
int *p = new A();
std::shared_ptr<int> sp(p);
// 3. 这样,就获得了一个新的sp2,并且sp2跟 sp 共享
std::shared_ptr<int> sp2 = p->get_sp();
2. 使用场景
如果一个类使用shared_ptr来管理,但是类中的成员函数需要将该对象作为参数传递给其他外部函数,就可以使用shared_from_this()
来处理。更具体些,在类内部需要调用异步函数的时候,异步函数执行的时间点不确定,在类内调用的时候可能只是设置个lambda,或者使用 std::bind 绑定一下,这时候需要将该对象使用 shared_ptr<> 传递给异步函数,做保活,保证异步函数在调用的时候,本对象还存活。
为什么不直接传递裸指针?
为了统一用智能指针来管理对象。只要在外部使用到该类的地方,都使用智能指针来处理。
为什么不使用 shared_ptr<A>(this)?
前边已经说过了,会产生两组不相关的shared_ptr,导致释放两次该对象。
使用示例如下:
class A;
void say_hello(std::shared_ptr<A> a)
{
}
// 1. A继承std::enable_shared_from_this<A>模板类
class A: std::enable_shared_from_this<A>
{
public:
A();
void say_hi()
{
// 2. 对外接口统一使用 shared_ptr来管理A
say_hello(shared_from_this());
}
private:
int a_;
};
3. 如何实现 shared_from_this()
大致如下:
shared_ptr<A *a> {
if (a 继承自 std::enable_shared_from_this) {
save_shared_ptr_info_into(a->weak_ptr_obj);
}
}
shared_ptr enable_shared_from_this::shared_from_this(){
return get_shared_ptr_from_info(weak_ptr_obj);
}
在构造shared_ptr<A> 的时候,判断A是否继承了 std::enable_shared_from_this,如果继承,就将 shared信息保存到 std::enable_shared_from_this 的一个成员 weak_ptr_obj 中。然后再调用 shared_from_this() 的时候,根据 weak_ptr_obj 从保存的信息中获取一个 shared_ptr。
参考
- C++11新特性之十:enable_shared_from_this
- shared_ptr 之shared_from_this
- 《inux多线程服务器端编程》 $1.11.1