zoukankan      html  css  js  c++  java
  • 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。

  • 相关阅读:
    HTML5中input输入框的种类
    perl mojo 编码
    perl encode_utf8 decode_utf8
    perl mojo use utf8 和no utf8
    perl unload oracle gbk
    perl unload Oracle utf8 数据库
    perl socket 返回发送成功数据的长度
    perl socket 客户端发送消息
    验证码识别(Tess4J初体验)
    Uploadify 3.2 参数属性、事件、方法函数详解
  • 原文地址:https://www.cnblogs.com/WindSun/p/14697066.html
Copyright © 2011-2022 走看看