首先要說明的一個問題是:如何安全地将this指針傳回給調用者。一般來說,我們不能直接将this指針傳回。想象這樣的情況,該函數将this指針傳回到外部某個變量儲存,然後這個對象自身已經析構了,但外部變量并不知道,此時如果外部變量使用這個指針,就會使得程式崩潰。
使用智能指針shared_ptr看起來是個不錯的解決方法。但問題是如何去使用它呢?我們來看如下代碼:

#include <iostream>
#include <boost/shared_ptr.hpp>
class Test
{
public:
//析構函數
~Test() { std::cout << "Test Destructor." << std::endl; }
//擷取指向目前對象的指針
boost::shared_ptr<Test> GetObject()
{
boost::shared_ptr<Test> pTest(this);
return pTest;
}
};
int main(int argc, char *argv[])
{
{
boost::shared_ptr<Test> p( new Test( ));
std::cout << "q.use_count(): " << q.use_count() << std::endl;
boost::shared_ptr<Test> q = p->GetObject();
}
return 0;
}

運作後,程式輸出:
Test Destructor.
q.use_count(): 1
Test Destructor.
可以看到,對象隻構造了一次,但卻析構了兩次。并且在增加一個指向的時候,shared_ptr的計數并沒有增加。也就是說,這個時候,p和q都認為自己是Test指針的唯一擁有者,這兩個shared_ptr在計數為0的時候,都會調用一次Test對象的析構函數,是以會出問題。
那麼為什麼會這樣呢?給一個shared_ptr<Test>傳遞一個this指針難道不能引起shared_ptr<Test>的計數嗎?
答案是:對的,shared_ptr<Test>根本認不得你傳進來的指針變量是不是之前已經傳過。
看這樣的代碼:

int main()
{
Test* test = new Test();
shared_ptr<Test> p(test);
shared_ptr<Test> q(test);
std::cout << "p.use_count(): " << p.use_count() << std::endl;
std::cout << "q.use_count(): " << q.use_count() << std::endl;
return 0;
}

運作後,程式輸出:
p.use_count(): 1
q.use_count(): 1
Test Destructor.
Test Destructor.
也證明了剛剛的論述:shared_ptr<Test>根本認不得你傳進來的指針變量是不是之前已經傳過。
事實上,類對象是由外部函數通過某種機制配置設定的,而且一經配置設定立即交給 shared_ptr管理,而且以後凡是需要共享使用類對象的地方,必須使用這個 shared_ptr當作右值來構造産生或者拷貝産生(shared_ptr類中定義了指派運算符函數和拷貝構造函數)另一個shared_ptr ,進而達到共享使用的目的。
解釋了上述現象後,現在的問題就變為了:如何在類對象(Test)内部中獲得一個指向目前對象的shared_ptr 對象?如果我們能夠做到這一點,直接将這個shared_ptr對象傳回,就不會造成建立的shared_ptr的問題了。
下面來看看enable_shared_from_this類的威力。
enable_shared_from_this 是一個以其派生類為模闆類型參數的基類模闆,繼承它,派生類的this指針就能變成一個 shared_ptr。
有如下代碼:

#include <iostream>
#include <boost/enable_shared_from_this.hpp>
#include <boost/shared_ptr.hpp>
class Test : public boost::enable_shared_from_this<Test> //改進1
{
public:
//析構函數
~Test() { std::cout << "Test Destructor." << std::endl; }
//擷取指向目前對象的指針
boost::shared_ptr<Test> GetObject()
{
return shared_from_this(); //改進2
}
};
int main(int argc, char *argv[])
{
{
boost::shared_ptr<Test> p( new Test( ));
std::cout << "q.use_count(): " << q.use_count() << std::endl;
boost::shared_ptr<Test> q = p->GetObject();
}
return 0;
}

運作後,程式輸出:
Test Destructor.
q.use_count(): 2;
可以看到,問題解決了!
接着來看看enable_shared_from_this 是如何工作的,以下是它的源碼:

template<class T> class enable_shared_from_this
{
protected:
BOOST_CONSTEXPR enable_shared_from_this() BOOST_SP_NOEXCEPT
{
}
BOOST_CONSTEXPR enable_shared_from_this(enable_shared_from_this const &) BOOST_SP_NOEXCEPT
{
}
enable_shared_from_this & operator=(enable_shared_from_this const &) BOOST_SP_NOEXCEPT
{
return *this;
}
~enable_shared_from_this() BOOST_SP_NOEXCEPT // ~weak_ptr<T> newer throws, so this call also must not throw
{
}
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;
}
weak_ptr<T> weak_from_this() BOOST_SP_NOEXCEPT
{
return weak_this_;
}
weak_ptr<T const> weak_from_this() const BOOST_SP_NOEXCEPT
{
return weak_this_;
}
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 BOOST_SP_NOEXCEPT
{
if( weak_this_.expired() )
{
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

标黃部分是shared_from_this()函數的實作。可以看到,這個函數使用一個weak_ptr對象(weak_this_)來構造一個 shared_ptr對象,然後将shared_ptr對象傳回。
注意這個weak_ptr是執行個體對象的一個成員變量,是以對于一個對象來說,它一直是同一個,每次在調用shared_from_this()時,就會根據weak_ptr來構造一個臨時shared_ptr對象。
也許看到這裡會産生疑問,這裡的shared_ptr也是一個臨時對象,和前面有什麼差別?還有,為什麼enable_shared_from_this 不直接儲存一個 shared_ptr 成員?
對于第一個問題,這裡的每一個shared_ptr都是根據weak_ptr來構造的,而每次構造shared_ptr的時候,使用的參數是一樣的,是以這裡根據相同的weak_ptr來構造多個臨時shared_ptr等價于用一個shared_ptr來做拷貝。(PS:在shared_ptr類中,是有使用weak_ptr對象來構造shared_ptr對象的構造函數的:
template<class Y>
explicit shared_ptr( weak_ptr<Y> const & r ): pn( r.pn )
)
對于第二個問題,假設我在類裡儲存了一個指向自身的shared_ptr,那麼這個 shared_ptr的計數最少都會是1,也就是說,這個對象将永遠不能析構,是以這種做法是不可取的。
在enable_shared_from_this類中,沒有看到給成員變量weak_this_初始化指派的地方,那究竟是如何保證weak_this_擁有着Test類對象的指針呢?
首先我們生成類T時,會依次調用enable_shared_from_this類的構造函數(定義為protected),以及類Test的構造函數。在調用enable_shared_from_this的構造函數時,會初始化定義在enable_shared_from_this中的私有成員變量weak_this_(調用其預設構造函數),這時的weak_this_是無效的(或者說不指向任何對象)。
接着,當外部程式把指向類Test對象的指針作為初始化參數來初始化一個shared_ptr(boost::shared_ptr<Test> p( new Test( ));)。
現在來看看 shared_ptr是如何初始化的,shared_ptr 定義了如下構造函數:
template<class Y>
explicit shared_ptr( Y * p ): px( p ), pn( p )
{
boost::detail::sp_enable_shared_from_this( this, p, p );
}
裡面調用了 boost::detail::sp_enable_shared_from_this :

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 )
{
pe->_internal_accept_owner( ppx, const_cast< Y* >( py ) );
}
}

裡面又調用了enable_shared_from_this 的 _internal_accept_owner :

template<class X, class Y> void _internal_accept_owner( shared_ptr<X> const * ppx, Y * py ) const
{
if( weak_this_.expired() )
{
weak_this_ = shared_ptr<T>( *ppx, py );
}
}

而在這裡,對enable_shared_from_this 類的成員weak_this_進行拷貝指派,使得weak_this_作為類對象 shared_ptr 的一個觀察者。
這時,當類對象本身需要自身的shared_ptr時,就可以從這個weak_ptr來生成一個了:
shared_ptr<T> shared_from_this()
{
shared_ptr<T> p( weak_this_ );
BOOST_ASSERT( p.get() == this );
return p;
}
以上。
從上面的說明來看,需要小心的是shared_from_this()僅在shared_ptr<T>的構造函數被調用之後才能使用,原因是enable_shared_from_this::weak_this_并不在構造函數中設定,而是在shared_ptr<T>的構造函數中設定。
是以,如下代碼是錯誤的:

class D:public boost::enable_shared_from_this<D>
{
public:
D()
{
boost::shared_ptr<D> p=shared_from_this();
}
};

原因是在D的構造函數中雖然可以保證enable_shared_from_this<D>的構造函數被調用,但weak_this_是無效的(還還沒被接管)。
如下代碼也是錯誤的:

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

原因同上。
總結為:不要試圖對一個沒有被shared_ptr接管的類對象調用shared_from_this(),不然會産生未定義行為的錯誤。
轉載自:https://www.cnblogs.com/codingmengmeng/p/9123874.html
僅作為學習筆記用,如果侵犯了作者的版權,請聯系删除。
參考:
Boost 庫 Enable_shared_from_this 實作原理分析
如何用enable_shared_from_this 來得到指向自身的shared_ptr及對enable_shared_from_this 的了解
enable_shared_from_this模闆類使用完全解析