C++11的enable_shared_from_this
crazyang Lv1

c++11 中的 shared_from_this() 来源于 boost 中的 enable_shared_form_this 类和 shared_from_this() 函数,功能为返回一个当前类的 std::share_ptr,使用方法如下:

1
2
3
4
5
6
7
8
#include<memory>
class Test: public std::enable_shared_from_this<Test>
{
public:
Test();
~Test();
std::shared_ptr<Test> getSharedFromThis() { return shared_from_this(); }
}

enable_shared_from_this 的由来

在智能指针的使用过程中,我们会遇到这样一种情况,我们在类的成员函数调用某一个函数,而该函数需要传递一个当前对象的智能指针作为参数时,我们需要能够在成员函数中获得自己的智能指针。

在多线程程序中也存在这样的应用,如果我们的线程函数是一个成员函数,可以把该对象的智能指针作为参数传递到线程函数中,这种做法是人为的增加了对象的引用计数,延长对象的生命周期,防止线程函数在执行的时候对象被释放而引发内存错误。总之就是我们在实际的编码中会存在各种各样的应用。

我们可能有两个疑惑:

  1. 把当前类对象作为参数传给其他函数时,为什么要传递 share_ptr 呢?直接传递 this 指针不可以吗?

    一个裸指针传递给调用者,谁也不知道调用者会干什么?假如调用者 delete 了该对象,而 share_tr此时还指向该对象。

  2. 这样传递 share_ptr 可以吗?share_ptr<this> ?

    这样会造成2个非共享的share_ptr指向一个对象,最后造成2次析构该对象。

我们不能人为地通过 this 来构造一个当前对象的 shared_ptr 指标,如下错误的做法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <memory>
class TestClass;

void Test(std::shared_ptr<TestClass> tt)
{

}

class TestClass
{
public:
TestClass() {}
~TestClass() {}
void TestPtr()
{
std::shared_ptr<TestClass> tt = std::shared_ptr<TestClass>(this);
Test(tt);
}
};

int main(int argc, char** argv)
{
std::shared_ptr<TestClass> t(new TestClass());
t->TestPtr();
return 0;
}

在 TestPtr() 函数中通过 this 构造出 shared_ptr,就相当于把自己的的控制权交给了这个临时变量 tt,一旦 tt 超出作用域就会释放,导致该对象也被释放。这是一个致命的错误。

为了解决这个问题,在 C++11 中提供了 enable_shared_from_this 这个模板类(boost 库很早就提供了这个模板类),自己的对象继承自 enable_shared_from_this 。enable_shared_from_this 提供了一个shared_from_this() 的方法返回自己的智能指针。与上面错误的例子区别在于,shared_from_this 会增加该对象的引用计数,而不是重新建立一个临时的 shared_ptr 来管理。看下面具体的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <memory>
class TestClass;

void Test(std::shared_ptr<TestClass> tt)
{

}

class TestClass : public std::enable_shared_from_this<TestClass>
{
public:
TestClass() {}
~TestClass() {}
void TestPtr()
{
std::shared_ptr<TestClass> tt = shared_from_this();
Test(tt);
}
};

int main(int argc, char** argv)
{
std::shared_ptr<TestClass> t(new TestClass());
t->TestPtr();
return 0;
}

shared_from_this() 函数

shared_from_this 的出现确实能够解决我们编码中所遇到的问题,但是它的坑也是比较多的。我们先来看看 enable_shared_from_this 这个对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
template<class _Ty> 
class enable_shared_from_this
{ // provide member functions that create shared_ptr to this
public:
typedef _Ty _EStype;

shared_ptr<_Ty> shared_from_this()
{ // return shared_ptr
return (shared_ptr<_Ty>(_Wptr));
}

shared_ptr<const _Ty> shared_from_this() const
{ // return shared_ptr
return (shared_ptr<const _Ty>(_Wptr));
}

protected:
enable_shared_from_this()
{ // construct (do nothing)
}

enable_shared_from_this(const enable_shared_from_this&)
{ // construct (do nothing)
}

enable_shared_from_this& operator=(const enable_shared_from_this&)
{ // assign (do nothing)
return (*this);
}

~enable_shared_from_this()
{ // destroy (do nothing)
}

private:
template<class _Ty1, class _Ty2>
friend void _Do_enable(
_Ty1 *,
enable_shared_from_this<_Ty2>*,
_Ref_count_base *);

mutable weak_ptr<_Ty> _Wptr;
};

template<class _Ty1, class _Ty2>
inline void _Do_enable(
_Ty1 *_Ptr,
enable_shared_from_this<_Ty2> *_Es,
_Ref_count_base *_Refptr)
{ // reset internal weak pointer
_Es->_Wptr._Resetw(_Ptr, _Refptr);
}

这是标准库的源码,我们看到在 enable_shared_from_this 内部储存了一个 weak_ptr。shared_from_this 函数就是通过这个 weak_ptr 得到了。但是另外一点,我们可以看到在enable_shared_from_this 的构造函数中并没有对这个 weak_ptr 进行初始化。

这就是为什么我们不能在构造函数调用 shared_from_this() 的原因,因为其内部的 weak_ptr 并没有初始化。所以会产生错误。

在实际的程序设计中如果我们需要在对象初始化中用到自己的 shared_ptr。可以单独将初始化操作放到一个独立的 init 函数中,这时候再调用 shared_from_this() 是没有问题的(但还是有点问题,下面会讲到)

熟悉 weak_ptr 的同学可能知道,我们在使用 weak_ptr 前,需要用一个 shared_ptr 来对其进行初始化。对 weak_ptr 初始化是要能获取到当前对象的引用计数对象,而引用计数对象可以通过 shared_ptr 对象获取到。当然我们同样可以用一个已经初始化过的 weak_ptr 来初始化另一个 weak_ptr,因为已初始化的weak_ptr 也可能获取到对象的引用计数。

enable_shared_from_this 内部的 weak_ptr 是通过_Do_enable函数初始化的。而_Do_enable函数实在shared_ptr 的构造函数中调用的,这是至关重要的一个环节。正因为如此我们在调用 shared_from_this 之前请确保程序已经显式地建立了 shared_ptr 对象,要不然enable_shared_from_this 内部的 weak_ptr 始终是无效。

下面具体举例说明的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class TestClass : public std::enable_shared_from_this<TestClass>
{
public:
TestClass()
{
}
~TestClass()
{
//TestClassPtr tt = shared_from_this();
}
void TestPtr()
{
std::shared_ptr<TestClass> tt = shared_from_this();
Test(tt);
}
};

int main()
{
TestClass t;
t.TestPtr(); //shared_from_this()错误

TestClass* t1(new TestClass());
t1->TestPtr();//shared_from_this()错误

std::shared_ptr<TestClass> t2(new TestClass());
t2->TestPtr(); //正确,已提前建立了shared_ptr
}

同理在析构函数中也不能调用 shared_from_this()。

在析构时,引用计数已经变为零,weak_ptr 已经相当于指向的是一个无效的对象,不能通过此无效的 weak_ptr 构造 shared_ptr。

  • 本文标题:C++11的enable_shared_from_this
  • 本文作者:crazyang
  • 创建时间:2020-03-25 12:00:14
  • 本文链接:https://blog.codepeak.cn/2020/03/25/C++11的enable_shared_from_this/
  • 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
 评论