0%

shared_from_this

1. 介绍

关于智能指针 shared_ptr 很有意思的一个问题。

可以用 std::shared_ptr 将裸指针封装起来,变成带引用计数的智能指针,当引用计数为0,C++会自动把裸指针指向的内存回收。

先定义一个类

1
2
3
4
5
6
7
class A
{
public:
A();
private:
int a_;
};

这里p是裸指针,sp是智能指针,管理p:

1
2
int *p = new A();
std::shared_ptr<int> sp(p); // sp的引用计数是1

现在新建个sp2,可以跟sp一起管理p了

1
2
3
int *p = new A();
std::shared_ptr<int> sp(p);
std::shared_ptr<int> sp2(sp); // sp 和 sp2的 引用计数都是2了

有个问题,如果再用sp2直接去管理p,会出现什么行为?

1
2
3
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 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,导致释放两次该对象。

使用示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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()

大致如下:

1
2
3
4
5
6
7
8
9
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。

参考

  1. C++11新特性之十:enable_shared_from_this
  2. shared_ptr 之shared_from_this
  3. 《inux多线程服务器端编程》 $1.11.1