Boost 庫 enable_shared_from_this 實現原理分析

使用情景:當類對象被 shared_ptr 管理時,需要在類自己定義的函數裏把當前類對象作爲參數傳給其他函數時,這時需要傳遞一個 shared_ptr ,否則就不能保持 shared_ptr 管理這個類對象的語義(因爲有一個 raw pointer 指向這個類對象,而 shared_ptr 對類對象的這個引用沒有計數,很有可能 shared_ptr 已經把類對象資源釋放了,而那個調用函數還在使用類對象——顯然,這肯定會產生錯誤)。 


很好奇這個模板類的實現。 

先看看怎麼使用: 

對一個類 A ,當我們希望使用 shared_ptr 來管理其類對象時,而且需要在自己定義的函數裏把類對象 shared_ptr (爲什麼不用普通指針,當我們使用智能指針管理資源時,必須統一使用智能指針,而不能在某些地方使用智能指針某些地方使用 raw pointer ,否則不能保持智能指針的語義,從而產生各種錯誤)傳給其他函數時,可以讓類 A 從 enable_shared_from_this 繼承: 

class A : public boost::enable_shared_from_this<A> { 
}; 

然後在類 A 中需要傳遞類對象本身 shared_ptr 的地方使用 shared_from_this 函數來獲得指向自身的 shared_ptr 。 

一個非常有代表性的例子: 

http://www.boost.org/doc/libs/1_39_0/doc/html/boost_asio/tutorial/tutdaytime3/src.html 

另《Beyond the C++ Standard Library》 shared_ptr 節也有很簡單明瞭的例子。 

實現原理: 

首先要考慮的是:在類對象本身當中不能存儲類對象本身的 shared_ptr ,否則類對象 shared_ptr 永遠也不會爲0了,從而這些資源永遠不會釋放,除非程序結束。 

其次:類對象肯定是外部函數通過某種機制分配的,而且一經分配立即交給 shared_ptr 管理(再次強調一遍:給 shared_ptr 管理的資源必須在分配時交給 shared_ptr ),而且以後凡是需要共享使用類對象的地方必須使用這個 shared_ptr 當作右值來構造產生或者拷貝產生另一個 shared_ptr 從而達到共享使用的目的。 

有了以上兩點的限制,要實現我們的目標(即在類對象內部使用類對象的 shared_ptr )有以下兩種方案: 

1、類對象的外部 shared_ptr 作爲函數參數傳給類的需要引用類對象自身的函數——顯然,這種方法很醜陋,而且並不是所有的情況都可行(如在外部 shared_ptr 不可見的作用域中就不行); 

2、類對象自身存儲某種信息,在需要自身 shared_ptr 時來產生一個臨時的 shared_ptr 。 

顯然,第2種方法更優雅(對於用戶來說),關鍵是信息怎麼存儲? 

對了, weak_ptr ! 

實際上, boost 中就是這樣實現的。 

但現在的問題是:何時初始化這個 weak_ptr ?因爲類對象生成時還沒有生成相應的用來管理這個對象的 shared_ptr 。 

boost 1.39.0 中是這樣實現的: 

首先生成類 A :會依次調用 enable_shared_from_this 的構造函數(定義爲 protected ),以及類 A 的構造函數。在調用 enable_shared_from_this 的構造函數時,會初始化定義在 enable_shared_from_this 中的 weak_ptr (調用其默認構造函數),這時這個 weak_ptr 是無效的(或者說不指向任何對象)。 

接着:外部程序會把指向類 A 對象的指針作爲初始化參數來初始化一個 shared_ptr 。 

現在來看看 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_ptr 進行拷貝賦值,使得整個 weak_ptr 作爲類對象  shared_ptr 的一個觀察者。 

這時,當類對象本身需要自身的 shared_ptr 時,就可以從這個 weak_ptr 來生成一個了。 

C++代碼  收藏代碼
  1. //  
  2. // server.cpp  
  3. // ~~~~~~~~~~  
  4. //  
  5. // Copyright (c) 2003-2008 Christopher M. Kohlhoff (chris at kohlhoff dot com)  
  6. //  
  7. // Distributed under the Boost Software License, Version 1.0. (See accompanying  
  8. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)  
  9. //  
  10.   
  11. #include <ctime>  
  12. #include <iostream>  
  13. #include <string>  
  14. #include <boost/bind.hpp>  
  15. #include <boost/shared_ptr.hpp>  
  16. #include <boost/enable_shared_from_this.hpp>  
  17. #include <boost/asio.hpp>  
  18.   
  19. using boost::asio::ip::tcp;  
  20.   
  21. std::string make_daytime_string()  
  22. {  
  23.   using namespace std; // For time_t, time and ctime;  
  24.   time_t now = time(0);  
  25.   return ctime(&now);  
  26. }  
  27.   
  28. class tcp_connection  
  29.   : public boost::enable_shared_from_this<tcp_connection>  
  30. {  
  31. public:  
  32.   typedef boost::shared_ptr<tcp_connection> pointer;  
  33.   
  34.   static pointer create(boost::asio::io_service& io_service)  
  35.   {  
  36.     return pointer(new tcp_connection(io_service));  
  37.   }  
  38.   
  39.   tcp::socket& socket()  
  40.   {  
  41.     return socket_;  
  42.   }  
  43.   
  44.   void start()  
  45.   {  
  46.     message_ = make_daytime_string();  
  47.   
  48.     boost::asio::async_write(socket_, boost::asio::buffer(message_),  
  49.         boost::bind(&tcp_connection::handle_write, shared_from_this(),  
  50.           boost::asio::placeholders::error,  
  51.           boost::asio::placeholders::bytes_transferred));  
  52.   }  
  53.   
  54. private:  
  55.   tcp_connection(boost::asio::io_service& io_service)  
  56.     : socket_(io_service)  
  57.   {  
  58.   }  
  59.   
  60.   void handle_write(const boost::system::error_code& /*error*/,  
  61.       size_t /*bytes_transferred*/)  
  62.   {  
  63.   }  
  64.   
  65.   tcp::socket socket_;  
  66.   std::string message_;  
  67. };  
  68.   
  69. class tcp_server  
  70. {  
  71. public:  
  72.   tcp_server(boost::asio::io_service& io_service)  
  73.     : acceptor_(io_service, tcp::endpoint(tcp::v4(), 13))  
  74.   {  
  75.     start_accept();  
  76.   }  
  77.   
  78. private:  
  79.   void start_accept()  
  80.   {  
  81.     tcp_connection::pointer new_connection =  
  82.       tcp_connection::create(acceptor_.io_service());  
  83.   
  84.     acceptor_.async_accept(new_connection->socket(),  
  85.         boost::bind(&tcp_server::handle_accept, this, new_connection,  
  86.           boost::asio::placeholders::error));  
  87.   }  
  88.   
  89.   void handle_accept(tcp_connection::pointer new_connection,  
  90.       const boost::system::error_code& error)  
  91.   {  
  92.     if (!error)  
  93.     {  
  94.       new_connection->start();  
  95.       start_accept();  
  96.     }  
  97.   }  
  98.   
  99.   tcp::acceptor acceptor_;  
  100. };  
  101.   
  102. int main()  
  103. {  
  104.   try  
  105.   {  
  106.     boost::asio::io_service io_service;  
  107.     tcp_server server(io_service);  
  108.     io_service.run();  
  109.   }  
  110.   catch (std::exception& e)  
  111.   {  
  112.     std::cerr << e.what() << std::endl;  
  113.   }  
  114.   
  115.   return 0;  
  116. }  
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章