【Boost】boost库中的小工具enable_shared_from_this
使用情景:
當類對象被 shared_ptr 管理時,需要在類自己定義的函數(shù)里把當前類對象作為參數(shù)傳給其他函數(shù)時,這時需要傳遞一個 shared_ptr ,否則就不能保持 shared_ptr 管理這個類對象的語義(因為有一個 raw pointer 指向這個類對象,而 shared_ptr 對類對象的這個引用沒有計數(shù),很有可能 shared_ptr 已經(jīng)把類對象資源釋放了,而那個調(diào)用函數(shù)還在使用類對象——顯然,這肯定會產(chǎn)生錯誤)。?
很好奇這個模板類的實現(xiàn)。?先看看怎么使用:?
對一個類 A ,當我們希望使用 shared_ptr 來管理其類對象時,而且需要在自己定義的函數(shù)里把類對象 shared_ptr (為什么不用普通指針,當我們使用智能指針管理資源時,必須統(tǒng)一使用智能指針,而不能在某些地方使用智能指針某些地方使用 raw pointer ,否則不能保持智能指針的語義,從而產(chǎn)生各種錯誤)傳給其他函數(shù)時,可以讓類 A 從 enable_shared_from_this 繼承:?
class A : public boost::enable_shared_from_this<A> { }; 然后在類 A 中需要傳遞類對象本身 shared_ptr 的地方使用 shared_from_this 函數(shù)來獲得指向自身的 shared_ptr 。?
一個非常有代表性的例子:?
http://www.boost.org/doc/libs/1_39_0/doc/html/boost_asio/tutorial/tutdaytime3/src.html?
// // server.cpp // ~~~~~~~~~~ // // Copyright (c) 2003-2008 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) //#include <ctime> #include <iostream> #include <string> #include <boost/bind.hpp> #include <boost/shared_ptr.hpp> #include <boost/enable_shared_from_this.hpp> #include <boost/asio.hpp>using boost::asio::ip::tcp;std::string make_daytime_string() {using namespace std; // For time_t, time and ctime;time_t now = time(0);return ctime(&now); }class tcp_connection: public boost::enable_shared_from_this<tcp_connection> { public:typedef boost::shared_ptr<tcp_connection> pointer;static pointer create(boost::asio::io_service& io_service){return pointer(new tcp_connection(io_service));}tcp::socket& socket(){return socket_;}void start(){message_ = make_daytime_string();boost::asio::async_write(socket_, boost::asio::buffer(message_),boost::bind(&tcp_connection::handle_write, shared_from_this(),boost::asio::placeholders::error,boost::asio::placeholders::bytes_transferred));}private:tcp_connection(boost::asio::io_service& io_service): socket_(io_service){}void handle_write(const boost::system::error_code& /*error*/,size_t /*bytes_transferred*/){}tcp::socket socket_;std::string message_; };class tcp_server { public:tcp_server(boost::asio::io_service& io_service): acceptor_(io_service, tcp::endpoint(tcp::v4(), 13)){start_accept();}private:void start_accept(){tcp_connection::pointer new_connection =tcp_connection::create(acceptor_.io_service());acceptor_.async_accept(new_connection->socket(),boost::bind(&tcp_server::handle_accept, this, new_connection,boost::asio::placeholders::error));}void handle_accept(tcp_connection::pointer new_connection,const boost::system::error_code& error){if (!error){new_connection->start();start_accept();}}tcp::acceptor acceptor_; };int main() {try{boost::asio::io_service io_service;tcp_server server(io_service);io_service.run();}catch (std::exception& e){std::cerr << e.what() << std::endl;}return 0; } 另《Beyond the C++ Standard Library》 shared_ptr 節(jié)也有很簡單明了的例子。?
使用注意點:
shared_from_this()在一個類中需要傳遞類對象本身shared_ptr的地方使用shared_from_this函數(shù)來獲得指向自身的shared_ptr,它是enable_shared_from_this<T>的成員函數(shù),返回shared_ptr<T>。
首先需要注意的是:這個函數(shù)僅在shared_ptr<T>的構(gòu)造函數(shù)被調(diào)用之后才能使用。原因是enable_shared_from_this::weak_ptr并不在enable_shared_from_this<T>構(gòu)造函數(shù)中設(shè)置,而是在shared_ptr<T>的構(gòu)造函數(shù)中設(shè)置。?
a) 如下代碼是錯誤的:
b) 如下代碼也是錯誤的:
[cpp]?view plain?copy ?print?
c) 如下代碼是正確的:
[cpp]?view plain?copy ?print?
1. 首先調(diào)用enable_shared_from_this<D>的構(gòu)造函數(shù);
2. 其次調(diào)用D的構(gòu)造函數(shù);
3. 最后調(diào)用shared_ptr<D>的構(gòu)造函數(shù)。
是第3個動作設(shè)置了enable_shared_from_this<D>的weak_ptr,而不是第1個動作。這個地方是很違背c++常理和邏輯的,必須小心。?
結(jié)論是:不要在構(gòu)造函數(shù)中使用shared_from_this;其次,如果要使用shared_ptr,則應(yīng)該在所有地方均使用,不能使用D d這種方式,也決不要傳遞裸指針。?
實現(xiàn)原理:?
首先要考慮的是:在類對象本身當中不能存儲類對象本身的 shared_ptr ,否則類對象 shared_ptr 永遠也不會為0了,從而這些資源永遠不會釋放,除非程序結(jié)束。?其次:類對象肯定是外部函數(shù)通過某種機制分配的,而且一經(jīng)分配立即交給 shared_ptr 管理(再次強調(diào)一遍:給 shared_ptr 管理的資源必須在分配時交給 shared_ptr ),而且以后凡是需要共享使用類對象的地方必須使用這個 shared_ptr 當作右值來構(gòu)造產(chǎn)生或者拷貝產(chǎn)生另一個 shared_ptr 從而達到共享使用的目的。?
有了以上兩點的限制,要實現(xiàn)我們的目標(即在類對象內(nèi)部使用類對象的 shared_ptr )有以下兩種方案:?
1、類對象的外部 shared_ptr 作為函數(shù)參數(shù)傳給類的需要引用類對象自身的函數(shù)——顯然,這種方法很丑陋,而且并不是所有的情況都可行(如在外部 shared_ptr 不可見的作用域中就不行);?
2、類對象自身存儲某種信息,在需要自身 shared_ptr 時來產(chǎn)生一個臨時的 shared_ptr 。?
顯然,第2種方法更優(yōu)雅(對于用戶來說),關(guān)鍵是信息怎么存儲??
對了, weak_ptr !?
實際上, boost 中就是這樣實現(xiàn)的。?
但現(xiàn)在的問題是:何時初始化這個 weak_ptr ?因為類對象生成時還沒有生成相應(yīng)的用來管理這個對象的 shared_ptr 。?
boost 1.39.0 中是這樣實現(xiàn)的:?
首先生成類 A :會依次調(diào)用 enable_shared_from_this 的構(gòu)造函數(shù)(定義為 protected ),以及類 A 的構(gòu)造函數(shù)。在調(diào)用 enable_shared_from_this 的構(gòu)造函數(shù)時,會初始化定義在 enable_shared_from_this 中的 weak_ptr (調(diào)用其默認構(gòu)造函數(shù)),這時這個 weak_ptr 是無效的(或者說不指向任何對象)。?
接著:外部程序會把指向類 A 對象的指針作為初始化參數(shù)來初始化一個 shared_ptr 。?
現(xiàn)在來看看 shared_ptr 是如何初始化的, shared_ptr 定義了如下構(gòu)造函數(shù):?
template<class Y> explicit shared_ptr( Y * p ): px( p ), pn( p ) { boost::detail::sp_enable_shared_from_this( this, p, p ); } 里面調(diào)用了? 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 ) ); } } 里面又調(diào)用了 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 來生成一個了。
總結(jié)
以上是生活随笔為你收集整理的【Boost】boost库中的小工具enable_shared_from_this的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 拦截聊天记录--Hook技巧简介【原创】
- 下一篇: C++ 学习之函数重载、基于const的