使用来自boost::asio
的异步I / O操作我经常需要共享指针(以及回调中的enable_shared_from_this
和shared_from_this
)以避免过早删除对象。
我认为可以通过在课堂上保留unique_ptr
或只是对象(所有权)(作为成员)来实现
例如:
只有foo
方法使用sender
。
第1(流行解决方案):
class C {
public:
void foo
{
std::shared_ptr<Sender> sender = std::make_shared<Sender>();
sender->send();
// class Sender use async_write
// inheritance: enable_shared_from_this
// callback in async operation created with argument shared_from_this
}
};
为什么没有人(?)使用此解决方案:
class D {
public:
void foo
{
sender.reset(new Sender);
sender->send();
}
private:
std::unique_ptr<Sender> sender;
};
我知道发件人不会被提前删除。我没有shared_ptrs。我认为避免使用它们是件好事,因为如果Sender类中的回调也使用异步操作,我需要另一个共享指针等。我认为D类更易于阅读。 但我想知道它是不是一个好的风格......我总是在网上看到与shared_ptrs的解决方案。
答案 0 :(得分:0)
对象只能有一个unique_ptr
。如果您同时发送和接收操作都会发生什么?如果您同时拥有接收操作和计时器会发生什么?
是的,您可以使用unique_ptr
,但是您必须实施自己的未完成操作数量计数。您必须以线程安全的方式递增和递减该计数。并且您必须不断检查该计数是否为零以调用析构函数。要清理它,您可能会将所有这些功能捆绑到自己的类中。而且你已经彻底改造了shared_ptr
。
答案 1 :(得分:0)
后一种用法很脆弱,无法保证不会过早删除发件人。例如,考虑在异步操作完成之前连续调用D::foo()
的情况。可以使用可能取决于Sender
对象的未完成操作删除先前的Sender
对象:
public: Sender
{
public:
void send()
{
boost::asio::async_write(socket_, boost::asio::buffer(buffer_), ...);
}
private:
std::array<char, 64> buffer_;
boost::asio::ip::tcp::socket socket_;
...
};
D d;
d.foo(); // Create Sender1, initiate async_write.
d.foo(); // Delete Sender1, create Sender2, initiate async_write.
io_service.run(); // Sender1's async_write invokes undefined behavior.
在上面的代码中,对d.foo()
的第二次调用将删除具有未完成操作的第一个Sender
对象,从而导致未定义的行为。另一方面,如果Sender
继承自enable_shared_from_this
,并且shared_from_this()
的结果绑定到异步操作的处理程序,那么Sender
对象的生命周期会延长至少与操作一样长。
答案 2 :(得分:0)
你看过asio :: spawn吗?基本上,它消除了异步C ++编程的大部分复杂性,并允许您将对象放在堆栈上。
以下是一个简单示例:modern.cpp
使用这种方法可以节省大量实现和调试异步应用程序的时间,并且使代码非常容易理解。 (我曾经使用传统的回调在TCP代理上实现了RTP。事后证明这是一个噩梦。)
关于分片与唯一指针 - 我的经验是共享指针更易于与asio一起使用。移动独特的指针存在一些问题,当程序变得稍微复杂时,很容易犯错误。我认为我会坚持使用共享和弱指针作为默认值 - 并且只切换到对象的唯一指针,其中探查器输出表明它值得一试。