c++11 enable_shared_from_this

本质的原因:raw data和引用计数的管理块,可能是分开的

使用场景: 需要在对象中得到shared ptr, 

错误的代码:直接构造新的shared_ptr<A>对象,两个shared_ptr objects 是独立的,pointer to the same raw mem. 两个独立的managed objects. 因此会导致undefined behaviour, delete raw data twice, 通常会导致crash.

class A {
public:
    std::shared_ptr<A> getShared() {
        return std::shared_ptr<A>(this);
    }
};
int main() {
    std::shared_ptr<A> pa = std::make_shared<A>();
    std::shared_ptr<A> pbad = pa->getShared();
    return 0;
}

具体场景: 为什么一定要在类的成员函数里面得到this的shared_ptr呢?直接去裸指针this为什么不行?在异步IO的编程中,callback函数通常在另外的线程中调用,跟主线程是分离开的,这是就涉及到一个对象生存期lifetime的概念,

 

//blocking GetRawData is trivial since it is done in child thread 
std::future<RedisRuslt> RedisConnector::AsyncSetDagToRedis(const SBOString&key, PDAG dag, CDBMEnv* env)
{
	assert(dag != nullptr);
	//start one thread to do the heavy setDagToRedis op
	PDAG tmpDag;
	dag->Duplicate(&tmpDag, dbmKeepData);
	std::shared_ptr<DAG> smartDag(tmpDag);
	return std::async(std::bind(&RedisConnector::AsyncSetSmartDagToRedis, shared_from_this(),key, smartDag, env));}
}  

分析:如果redisconnector对象在async函数调用之前就析构了,那么另外线程中调用bind函数时候,this指针就会失效。因此用shared_from_this(),*this对象生存期得到延长.

 boost库的example,是shared_from_this()典型的场景 https://www.boost.org/doc/libs/1_39_0/doc/html/boost_asio/tutorial/tutdaytime3/src.html  

实现原理:利用weak ptr作为桥梁,在需要shared_ptr时,通过weak_ptr构造出临时的shared_ptr对象

那么weak_ptr何时初始化呢?为什么时候指向shared_ptr呢,显然需要被shared_ptr管理的类对象需要基础一个enable_shared_from_this<T>的基类,基类中有weak_ptr这个class member,初始化的时机,应该在shared_ptr<A>(new A())的时候.

由此引申出引用的错误案例:

class D:public boost::enable_shared_from_this<D>
{
public:
    D()
    {
        boost::shared_ptr<D> p=shared_from_this();
    }
    void func()
    {
        boost::shared_ptr<D> p=shared_from_this();
    }
    
};
int main(){
   D num;
   num.func();
}

从上面可以得到,enable_shared_from_this 的成员 weakthis 不是在 enable_shared_from_this 的构造函数中初始化的,而是通过 _internal_accept_owner 来赋初值的。而这个函数是 shared_ptr 在初始化的时候调用的。得出结论:不要在构造函数中使用 shared_from_this.

如果有多重继承的情况,而且不同的父类都继承了 boost::enable_shared_from_this,则会出现比较怪异的情况,多重继承在 C++ 中原本就是不推荐的,所以应该在应用中尽量避免这种情况。

 

源码:

template<class Y>
explicit shared_ptr( Y * p ): px( p ), pn( p ) // Y must be complete
{
    boost::detail::sp_enable_shared_from_this( this, p, p );
}

template< class X, class Y, class T > inline void sp_enable_shared_from_this( boost::shared_ptr<X> const * ppx, Y const * py, boost::enable_shared_from_this< T > const * pe )
{
    if( pe != 0 )
    {
      //调用 enable_shared_from_this对象的函数  
      pe->_internal_accept_owner( ppx, const_cast< Y* >( py ) );
    }
}


namespace boost
{

template<class T> class enable_shared_from_this
{
protected:

    enable_shared_from_this()
    {
    }

    enable_shared_from_this(enable_shared_from_this const &)
    {
    }

    enable_shared_from_this & operator=(enable_shared_from_this const &)
    {
        return *this;
    }

    ~enable_shared_from_this()
    {
    }

public:

    shared_ptr<T> shared_from_this()
    {
        shared_ptr<T> p( weak_this_ );
        BOOST_ASSERT( p.get() == this );
        return p;
    }

    shared_ptr<T const> shared_from_this() const
    {
        shared_ptr<T const> p( weak_this_ );
        BOOST_ASSERT( p.get() == this );
        return p;
    }

public: // actually private, but avoids compiler template friendship issues

    // Note: invoked automatically by shared_ptr; do not call
    template<class X, class Y> void _internal_accept_owner( shared_ptr<X> const * ppx, Y * py ) const
    {
        if( weak_this_.expired() )
        {
           //shared_ptr通过拷贝构造一个临时的shared_ptr,然后赋值给weak_ptr 
           weak_this_ = shared_ptr<T>( *ppx, py );
        }
    }

private:

    mutable weak_ptr<T> weak_this_;
};

} // namespace boost

#endif  // #ifndef BOOST_SMART_PTR_ENABLE_SHARED_FROM_THIS_HPP_INCLUDED

A common implementation for enable_shared_from_this is to hold a weak reference (such as std::weak_ptr) to this. The constructors of std::shared_ptr detect the presence of an enable_shared_from_this base and assign the newly created 

 参考链接:

https://www.cnblogs.com/codingmengmeng/p/9123874.html

https://github.com/prehistoric-penguin/blog-post/blob/master/2015-05-22-implementation-enable_shared_from_this.md

http://www.gaccob.com/publish/2013-08-11-boost-smart-ptr.html

https://blog.csdn.net/ithiker/article/details/51532484

https://www.cnblogs.com/lzjsky/archive/2011/05/05/2037363.html

 

 

 

posted @ 2019-02-25 13:09  kkshaq  阅读(1037)  评论(0编辑  收藏  举报