我有一个能够通过 TCP 调度消息的类。这里简化的界面:

class CommandScreenshot : public CameraCommand
{
public:
    CommandScreenshot();
    ~CommandScreenshot();
    void Dispatch(boost::shared_ptr<boost::asio::io_service> io_service);

 private:
     void resolve_handler(const boost::system::error_code& err,
          boost::asio::ip::tcp::resolver::iterator endpoint_iterator);

};

正如你所看到的,我有一个函数 Dispatch ,它实际上只是为了启动异步操作:
void CommandScreenshot::Dispatch(boost::shared_ptr<boost::asio::io_service> io_service)
{
    boost::asio::ip::tcp::resolver resolver(*io_service);
    boost::asio::ip::tcp::resolver::query query(m_hostname,"http");
    resolver.async_resolve(query,boost::bind(&CommandScreenshot::resolve_handler,this,boost::asio::placeholders::error, boost::asio::placeholders::iterator));
    return;
}

其他一切都将在以下回调函数中完成。 io_service 对象以及相应的线程由另一个类(具有 CommandScreenshot 的实例并调用 Dispatch 函数)管理。

现在要使用 Boost 实现一个简单的 TCP 连接,您需要一个 resolver 和一个 socket 对象,它们都绑定(bind)到 io_service 对象。由于io_service对象只会在那个时候被传递,函数被调用,我不能在类构造函数中初始化它们。
也不可能将它们声明为类成员,然后在函数本身中初始化它们。

我的第一个想法是在函数调用时初始化它们并将它们传递给我的完成处理程序。这意味着每次调用函数时我都会声明两个对象并将它们绑定(bind)到 io_service 。然后在 async_resolve ,我通过 boost::bind 将两者添加为参数。这意味着我的 resolve_handler 需要更多参数 - 例如:
void resolve_handler(const boost::system::error_code& err,
          boost::asio::ip::tcp::resolver::iterator endpoint_iterator,
          boost::asio::ip::tcp::resolver resolver,
          boost::asio::ip::tcp::socket socket);

我实际上怀疑这是一个体面和公平的解决方案。通常这些对象应该作为成员保留而不是被复制 - 所以我又想了一下,我的思绪把我带到了 boost::shared_ptr

在我的标题中,它现在看起来像这样:
// Stuff above stays the same
private:
  boost::shared_ptr<boost::asio::ip::tcp::resolver> m_resolver;
  boost::shared_ptr<boost::asio::ip::tcp::socket> m_socket;
// Stuff below stays the same

实现将是:
void CommandScreenshot::Dispatch(boost::shared_ptr<boost::asio::io_service> io_service)
{
    m_resolver.reset(new boost::asio::ip::tcp::resolver(*io_service));
    m_socket.reset(new boost::asio::ip::tcp::socket(*io_service));
    boost::asio::ip::tcp::resolver::query query(m_hostname,"http");
    m_resolver->async_resolve(query,boost::bind(&CommandScreenshot::resolve_handler,this,boost::asio::placeholders::error, boost::asio::placeholders::iterator));
    return;
}

有了这个,我不需要复制 4 个(甚至更多)参数并将它们绑定(bind)在一起。当我需要套接字对象时,我可以通过指针访问它,指针是一个类成员。

现在我的简单问题是 -> 这是正确的方法吗?即使异步部分没有完成,该函数也可以被多次调用。 (我知道我应该用互斥锁保护套接字和解析器)。但这是否干净,我每次调用 Dispatch 函数时都会创建一个新对象? reset 调用是否足以消除任何不需要的内存?

我知道这是一个针对特定短问题的长文本,而且本身甚至没有错误。但我总是想知道这是否是我要走的体面的方式,如果有更好的方式,我会怎么做。

最佳答案

将成员定义为 asio 对象的 shared_ptr 的想法是可以的。但是您不应该每次都再次销毁和创建它们:

if (!m_resolver)
{
  m_resolver.reset(...);
}

此外,如果确保对 asio 对象的所有操作都在运行 io_service 的线程中进行(假设每个 io_service 有一个线程),则可以避免显式锁定。为此,只需分离接口(interface)函数的实现并使用 post()。当然,使用 shared_from_this 习惯用法来简化对象生命周期控制:
void CommandScreenshot::someMethod(Arg1 arg1, Arg2 arg2)
{
  io_.post(bind(&CommandScreenshot::someMethodImpl, shared_from_this, arg1, arg2));
}
//...
void CommandScreenshot::someMethodImpl(Arg1 arg1, Arg2 arg2)
{
  // do anything you want with m_resolver, m_socket etc.
}

关于c++ - Boost Asio - 使用 shared_ptr 处理解析器和套接字,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/9177536/

10-11 18:26