C++11的enable_shared_from_this

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

#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 指标,如下错误的做法:

#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 来管理。看下面具体的例子:

#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 这个对象:

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 始终是无效。

下面具体举例说明的:

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。

posted @ 2021-04-24 15:50  WindSun  阅读(181)  评论(0编辑  收藏  举报
博客已停更,文章已转移,点击访问