您的位置:首页 > 其它

【Boost】boost库中的小工具enable_shared_from_this

2016-02-05 10:29 288 查看
使用情景:

当类对象被 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  //
// 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 节也有很简单明了的例子。 

使用注意点:

shared_from_this()在一个类中需要传递类对象本身shared_ptr的地方使用shared_from_this函数来获得指向自身的shared_ptr,它是enable_shared_from_this<T>的成员函数,返回shared_ptr<T>。
首先需要注意的是:这个函数仅在shared_ptr<T>的构造函数被调用之后才能使用。原因是enable_shared_from_this::weak_ptr并不在enable_shared_from_this<T>构造函数中设置,而是在shared_ptr<T>的构造函数中设置。 
a) 如下代码是错误的:

[cpp] view
plain copy

 print?

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_ptr还没有设置。 
b) 如下代码也是错误的:

[cpp] view
plain copy

 print?

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();  

}  

错误原因同上。 
c) 如下代码是正确的:

[cpp] view
plain copy

 print?

void main()  

{  

    boost::shared_ptr<D> d(new D);  

    d->func();  

}  

这里boost::shared_ptr<D> d(new D)实际上执行了3个动作:
1. 首先调用enable_shared_from_this<D>的构造函数;
2. 其次调用D的构造函数;
3. 最后调用shared_ptr<D>的构造函数。
是第3个动作设置了enable_shared_from_this<D>的weak_ptr,而不是第1个动作。这个地方是很违背c++常理和逻辑的,必须小心。 

结论是:不要在构造函数中使用shared_from_this;其次,如果要使用shared_ptr,则应该在所有地方均使用,不能使用D d这种方式,也决不要传递裸指针。 

实现原理: 

首先要考虑的是:在类对象本身当中不能存储类对象本身的 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 来生成一个了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: