我经常看到在码本模式,装订shared_from_this
作为第一个参数,以一个成员函数和使用调度结果async_*
函数。 下面是另一个问题的例子:
void Connection::Receive()
{
boost::asio::async_read(socket_,boost::asio::buffer(this->read_buffer_),
boost::bind(&Connection::handle_Receive,
shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
唯一的理由使用shared_from_this()
而不是this
是保持对象的生命,直到成员函数被调用。 但是,除非有某种神奇的提振地方,因为this
指针类型的Connection*
,这是所有handle_Receive
可以采取,并返回智能指针应立即转换为常规指针。 如果出现这种情况,没有什么可以保持物体活着。 而且,当然,还有呼吁没有指针shared_from_this
。
不过,我经常看到这种模式,我简直不敢相信,因为在我看来,它是完全打破。 是否有造成的shared_ptr稍后转换为常规指针,在操作完成时一些加速的魔力? 如果是这样,这是记录在案的地方?
尤其是它的地方记载,共享的指针将继续存在,直到操作完成? 调用get_pointer
雄厚的指针,然后调用返回的指针的成员函数,除非强有力的指针不被破坏,直到成员函数返回是不够的。
总之, boost::bind
创建的副本boost::shared_ptr<Connection>
是从返回shared_from_this()
和boost::asio
可以创建处理程序的副本。 直到发生下列情况之一的处理程序的副本将保持活动:
- 该处理器已被称为从该服务的一个线程
run()
run_one()
poll()
或poll_one()
成员函数被调用。 - 该
io_service
被破坏。 - 该
io_service::service
拥有该处理器是通过关闭shutdown_service()
下面是从文档相关的摘录:
虽然日(2007年),将用于TR2(修订版1)网络图书馆的提案是由Boost.Asio的衍生。 第5.3.2.7. Requirements on asynchronous operations
5.3.2.7. Requirements on asynchronous operations
提供了参数的一些细节async_
功能:
在本节中,异步操作是由一个名为前缀为功能启动async_
。 这些功能应被称为启动功能 。 [...]库实施可能使处理程序参数的副本,和原来的处理程序参数和所有副本是可以互换的。
参数,以启动功能的寿命应视为如下:
- 如果该参数被声明为const引用或按值[...]的实施可能使论点的副本,以及所有副本应比处理程序的调用后立即不迟被破坏。
[...]由库实现与所述发起函数的参数相关联的功能进行的任何呼叫都将在一个呼叫顺序1执行的,使得发生呼叫到n调用,其中对于所有的i,1≤I <N,调用我先于调用I + 1。
从而:
- 实现可以创建处理程序的副本。 在该例子中,复制的处理程序将创建的副本
shared_ptr<Connection>
,增加的引用计数Connection
实例,同时处理程序的副本仍然存活。 - 实施可能会破坏之前调用处理程序 处理 。 出现这种情况,如果异步操作是优秀的,当
io_serive::service
是关闭或io_service
被破坏。 在该示例中,处理程序的副本将被破坏,降低的引用计数Connection
,并有可能导致Connection
实例被销毁。 - 如果处理程序被调用,然后处理程序的所有副本将立即被从被处理程序执行返回被毁。 再次,处理程序的副本将被破坏,减少的引用计数
Connection
,并有可能导致其被破坏。 - 与所述相关联的功能
asnyc_
的参数,会按顺序执行的,而不是同时的。 这包括io_handler_deallocate
和io_handler_invoke
。 这保证了当正在调用处理程序的处理程序将不会被释放。 在大多数地区boost::asio
实现, 处理程序被复制或移动到堆栈变量,允许一次执行退出在其被宣布块发生破坏。 在该示例中,这确保了对引用计数Connection
将是处理程序的调用期间的至少一个。
它是这样的:
1)Boost.Bind文档以状态 :
“[注:的mem_fn创建功能对象能够接受的指针,参考,或智能对象指针作为它的第一个参数;对于附加信息,请参阅的mem_fn文档。]”
2)的mem_fn文档说 :
当功能对象与第一自变量x既不是指针也不到适当的类(在上面的例子中X)的引用调用时 ,它使用get_pointer(X),以获得从x中的指针。 库作者可以通过提供适当的get_pointer过载,允许的mem_fn识别并支持它们的“登记”他们的智能指针类。
所以,指针或智能指针存储在粘结剂原样,直到它的调用。
我也看惯了很多,(感谢@Tanner)我可以看到,为什么当它的使用这种模式io_service
在多个线程运行。 不过,我觉得还是有它的寿命问题,因为它取代了潜在的崩溃与一个潜在的内存/资源泄漏...
由于提高::绑定,绑定到shared_ptrs任何回调成为“用户”的对象(增加对象use_count),所以对象将不会被删除,直到所有未完成的回调已被调用。
该回调了boost :: ASIO ::异步*函数被调用时,取消或关闭时呼吁相关定时器或插座。 通常情况下,你只想让使用Stroustrup的心爱的析构函数相应取消/关闭调用RAII模式; 任务完成。
然而,析构函数不会被当主人删除对象调用,因为回调仍持有shared_ptrs的副本,所以他们的use_count将大于零,造成资源泄漏。 泄漏可以通过使适当的避免消除/之前删除物体接近呼叫。 但它并不像防呆如使用RAII,使在析构函数取消/关闭调用。 确保资源始终会释放,即使在异常的情况下。
一个符合RAII图案是使用静态函数回调并传递的weak_ptr注册回调函数,如下面的例子中,当以提高::绑定:
class Connection : public boost::enable_shared_from_this<Connection>
{
boost::asio::ip::tcp::socket socket_;
boost::asio::strand strand_;
/// shared pointer to a buffer, so that the buffer may outlive the Connection
boost::shared_ptr<std::vector<char> > read_buffer_;
void read_handler(boost::system::error_code const& error,
size_t bytes_transferred)
{
// process the read event as usual
}
/// Static callback function.
/// It ensures that the object still exists and the event is valid
/// before calling the read handler.
static void read_callback(boost::weak_ptr<Connection> ptr,
boost::system::error_code const& error,
size_t bytes_transferred,
boost::shared_ptr<std::vector<char> > /* read_buffer */)
{
boost::shared_ptr<Connection> pointer(ptr.lock());
if (pointer && (boost::asio::error::operation_aborted != error))
pointer->read_handler(error, bytes_transferred);
}
/// Private constructor to ensure the class is created as a shared_ptr.
explicit Connection(boost::asio::io_service& io_service) :
socket_(io_service),
strand_(io_service),
read_buffer_(new std::vector<char>())
{}
public:
/// Factory method to create an instance of this class.
static boost::shared_ptr<Connection> create(boost::asio::io_service& io_service)
{ return boost::shared_ptr<Connection>(new Connection(io_service)); }
/// Destructor, closes the socket to cancel the read callback (by
/// calling it with error = boost::asio::error::operation_aborted) and
/// free the weak_ptr held by the call to bind in the Receive function.
~Connection()
{ socket_.close(); }
/// Convert the shared_ptr to a weak_ptr in the call to bind
void Receive()
{
boost::asio::async_read(socket_, boost::asio::buffer(read_buffer_),
strand_.wrap(boost::bind(&Connection::read_callback,
boost::weak_ptr<Connection>(shared_from_this()),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred,
read_buffer_)));
}
};
注: read_buffer_
存储为shared_ptr
在Connection类并传递给read_callback
功能作为shared_ptr
。
这是为了确保在多个io_services
在单独的任务运行时, read_buffer_
不会被删除,直到其他任务完成后 ,即当read_callback
函数被调用。
有没有从转换boost::shared_ptr<Connection>
(返回类型shared_from_this
)来Connection*
(类型this
),因为这将是不安全的,你理所当然地指出。
神奇的是在Boost.Bind。 简单地说,在以下形式的呼叫bind(f, a, b, c)
无占位符或涉及此示例嵌套bind表达式)其中f
是指向部件,然后调用调用的结果将导致在以下形式的呼叫(a.*f)(b, c)
如果a
具有从所述类型的指针的衍生于构件(或类型的类型boost::reference_wrapper<U>
或者它的形式的((*a).*f)(b, c)
这适用于指针和智能指针的一致好评。 (实际上,我从内存工作规则std::bind
,Boost.Bind并不完全相同,但都是以同样的精神。)
此外,结果shared_from_this()
被存储在呼叫的结果bind
,确保没有问题的寿命。
也许我缺少的东西在这里很明显,但返回的shared_ptr shared_from_this()
存储在返回的函数对象boost::bind
,保持它活着。 这只是隐式转换为Connection*
在回调时启动的时候读的异步完成,并且对象保持活动的通话至少持续时间。 如果handle_Receive
不会创建从该另一个shared_ptr的,而储存在绑定仿函数shared_ptr的是最后的shared_ptr活着,对象将回调返回后予以销毁。