Boost 库 enable_shared_from_this 实现原理分析

来源:互联网 发布:金百福珠宝软件是什么 编辑:程序博客网 时间:2024/06/06 03:21

使用情景:当类对象被 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. }  
0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 陌陌当日打招呼人数已达上限怎么办 新生婴儿白天睡觉晚上不睡怎么办 被有翅膀的蚂蚁咬了怎么办 被带翅膀的蚂蚁蛰了怎么办 被长翅膀的蚂蚁咬了怎么办 宝宝被带翅膀的蚂蚁咬了怎么办 新的小白鞋鞋子有点夹脚怎么办 单反快门按不下去里面响怎么办 在上海动漫城游戏厅里输了钱怎么办 电缝纫机扎快了上线有套那是怎么办 老式缝纫机底线总夹梭子上怎么办 两个月宝宝脸一边大一边小怎么办 炒股入金10万亏3万怎么办 如果一支股票有好几个庄家怎么办 浦发信用卡查询密码被锁定怎么办 浦发信用卡输错密码被锁定怎么办 浦发手机银行查询密码锁定怎么办 宝宝吃母乳没吃饱就睡着了怎么办 量柱画线不是方屏电脑怎么办 夏天卧室开空调太干了怎么办 健身房锻炼手臂抻的肌肉疼怎么办 人累了可以休息 心累了怎么办 上来两天班感觉不想做了怎么办 休产假的时候公司解散了怎么办 上三天班老板不给结工资怎么办 老师说孩子上课纪律不好我该怎么办 大班的孩子记不住拼音怎么办呢 家长跟孩子沟通出现问题该怎么办? 如果孩子入学分配出现问题该怎么办 2岁多的宝宝喜欢动手打人怎么办 儿童新长出的大门牙像两边撇怎么办 被烫伤了怎么办的活动反思怎么写 生完孩子脸上起蝴蝶斑了怎么办 鼻子部位突然长了晒斑怎么办 我脸上长有日晒斑.该怎么办 做为小领导同事不听你的怎么办 二年级的小孩叫写作业不听怎么办 苹果手机微信出现黑框怎么办 百度网盘下载原画视频会闪退怎么办 已发布的公众号推文段落重复怎么办 谷歌商店找不到方舟手游怎么办