boost::shared_ptr递归调用解决办法--boost::enable_shared_from_this

用处:boost::enable_shared_from_this主要用于类中递归使用shared_ptr来管理本类。

下面转自:http://monkeycn.iteye.com/blog/1135901

使用情景:当类对象被 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.   using namespace std; // For time_t, time and ctime;  
  23.   time_t now = time(0);  
  24.   return ctime(&now);  
  25. }  
  26.   
  27. class tcp_connectionpublic boost::enable_shared_from_this<tcp_connection>  {  
  28. public:  
  29.   typedef boost::shared_ptr<tcp_connection> pointer;  
  30.   
  31.   static pointer create(boost::asio::io_service& io_service)  {  
  32.     return pointer(new tcp_connection(io_service));  
  33.   }  
  34.   
  35.   tcp::socket& socket()   {  
  36.     return socket_;  
  37.   }  
  38.   
  39.   void start()    {  
  40.     message_ = make_daytime_string();  
  41.   
  42.     boost::asio::async_write(socket_, boost::asio::buffer(message_),  
  43.         boost::bind(&tcp_connection::handle_write, shared_from_this(),  
  44.           boost::asio::placeholders::error,  
  45.           boost::asio::placeholders::bytes_transferred));  
  46.   }  
  47.   
  48. private:  
  49.   tcp_connection(boost::asio::io_service& io_service): socket_(io_service)   {}  
  50.   
  51.   void handle_write(const boost::system::error_code& /*error*/,  
  52.       size_t /*bytes_transferred*/)  { }  
  53.   
  54.   tcp::socket socket_;  
  55.   std::string message_;  
  56. };  
  57.   
  58. class tcp_server  {  
  59. public:  
  60.   tcp_server(boost::asio::io_service& io_service) : acceptor_(io_service, tcp::endpoint(tcp::v4(), 13))  {  
  61.     start_accept();  
  62.   }  
  63.   
  64. private:  
  65.   void start_accept()   {  
  66.     tcp_connection::pointer new_connection =  
  67.       tcp_connection::create(acceptor_.io_service());  
  68.   
  69.     acceptor_.async_accept(new_connection->socket(),  
  70.         boost::bind(&tcp_server::handle_accept, this, new_connection,  
  71.           boost::asio::placeholders::error));  
  72.   }  
  73.   
  74.   void handle_accept(tcp_connection::pointer new_connection,  
  75.       const boost::system::error_code& error)   {  
  76.     if (!error)  {  
  77.       new_connection->start();  
  78.       start_accept();  
  79.     }  
  80.   }  
  81.   
  82.   tcp::acceptor acceptor_;  
  83. };  
  84.   
  85. int main(int argc, char *argv[])  {  
  86.   try   {  
  87.     boost::asio::io_service io_service;  
  88.     tcp_server server(io_service);  
  89.     io_service.run();  
  90.   }  catch (std::exception& e)  {  
  91.     std::cerr << e.what() << std::endl;  
  92.   }  
  93.   
  94.   return 0;  
  95. }  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值