2

boost::asio のチュートリアルの1ページを見ていました。

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_.get_io_service()); // shared_ptr got created.
    acceptor_.async_accept(new_connection->socket(),
      boost::bind(&tcp_server::handle_accept, this, new_connection,
      boost::asio::placeholders::error)); // instance added to io_service task list, but bind does not use shared_ptr internally I believe.
  } // shared_ptr of tcp_connection goes out of scope.

  void handle_accept(tcp_connection::pointer new_connection,
  const boost::system::error_code& error)
  {
    if (!error)
    {
      new_connection->start();
    }
    start_accept();
  }

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_;
};

shared_ptrそして、オブジェクトのtcp_connectionオブジェクトが生きていない実行時間の一部があることがわかりました。tcp_connectionこれは、オブジェクトのカウントがゼロになるため、その部分の開始時にオブジェクトが破棄されることを意味しているように見えますがshared_ptr、これは明らかに私たちが望んでいることではありません。

tcp_connectionしかし、その後、クラスの引用でコメントを見ました

これを参照する操作がある限り、tcp_connection オブジェクトを存続させたいので、shared_ptr と enable_shared_from_this を使用します。

また、この問題についても検索を行い、SO hereで Q&A を得ました。しかし、私はまだタイトルの質問に戸惑っています。具体的にはどういう意味there is an operation that refers to itですか?tcp_server::start_accept()返された時点で、すべてshared_ptrtcp_connectionインスタンスがスコープ外になるはずであり、おそらくいくつかの生のポインター参照のみがio_serviceタスク リストに追加されました。このオブジェクトのインスタンスがない場合enabled_shared_from_this、ヒープ インスタンスがtcp_connection破棄されるのを防ぐにはどうすればよいですか? それとも、enabled_shared_from_this とは関係ありませんが、境界付きの async_handler を内部的に保持していますか?shared_ptrtcp_connectionboost::asio::io_serviceshared_ptr

4

2 に答える 2

3

オブジェクトは、ファンクタによって維持されています。

handle_acceptコールバック関数がshared_ptrtotcp_connectionを引数として取る方法に注意してください。boost::bindへのコールバックを指定するために使用される呼び出し内で魔法が発生するようになりましたasync_accept

その bind 呼び出しは、handle_accept value によって呼び出すために必要なすべての引数を格納する functor オブジェクトを返します。したがって、bind によって返されるファンクタにはshared_ptrtoのコピーが含まれているため、totcp_connectionが生き続けます。ファンクタ ( と考えてください) は、accept 操作が完了すると、コールバックを実行できるようにboost::functionによってコピーされます。async_acceptio_service

したがって、所有権の連鎖は次のio_serviceとおりshared_ptrですtcp_connection。ちなみに、同じことがなくenable_shared_from_thisても機能します。

于 2013-10-16T08:00:45.507 に答える
1

この質問は、指定されたオブジェクトの「いかなる」コードにも触れないことを意味する、shared_ptr の邪魔にならない設計を説明することで答えられるかもしれません。

邪魔にならないデザインから多くの長所を得ることができます. しかし一方で、それは必然的に状況につながります: 1 回の shared_ptr、shared_ptr は生のポインターなしでどこにでもあります。

このスニペットを見てみましょう:

{
    shared_ptr<int> orig_sp{new int};
    shared_ptr<int> good_sp{orig_sp};       // it's ok
    shared_ptr<int> bad_sp{orig_sp.get()};  // leads crashing
}

このスニペットは、同じ raw pointer( ) に対して削除を 2 回呼び出すため、プログラムをクラッシュさせますnew int。根本的な原因は、orig_sp参照カウントを と共有しているのに、 とは共有good_spしていないことbad_spです。つまり、すでに編集されている生のポインターから shared_ptr を作成することはできません。shared_ptr

次に、ASIO サンプル コードに戻ります。new_connectionのライフサイクルをよりも長くすることはすでによく知られていますtcp_server::start_accept()。この場合、shared_ptr は、ライフサイクルを正確に延長するための非常に便利なテンプレートです。

void handler(shared_ptr<vector<char>> buffer, /* other declarations */)
{
    // works after write.
    // buffer will be deleted automatically.
}

int main()
{
    shared_ptr<vector<char>> buffer{new vector<char>};

    // preparing buffer

    boost::asio::async_write(socket_, boost::asio::buffer(*buffer),
      boost::bind(handler, buffer, boost::asio::placeholders::error,
        boost::asio::placeholders::bytes_transferred));
}

しかし、問題は、shared_ptred オブジェクトのメンバー関数が自身のライフサイクルを延長したいということです。

class tcp_connection {
    void start()
    {
        boost::asio::async_write(socket_, boost::asio::buffer(message_),
          boost::bind(&tcp_connection::handle_write, (/* how to fill this field? */),
          boost::asio::placeholders::error,
          boost::asio::placeholders::bytes_transferred));
    }
};
  • そのフィールドへの入力は、 tcp_connectionthisのライフ サイクルを延長しないため、受け入れられません。
  • 詰め込みboost::shared_ptr<tcp_connection>(this)も不可です。最初のスニペットによると、クラッシュが発生します。
  • おそらくboost::shared_ptr<tcp_connection>(tcp_connection::create(acceptor_.get_io_service()))良さそうに見えますが、そのメンバーにオブジェクト自体を作成しようとしています。

今、それは非常に明確です。tcp_connection::start()どの shared_ptr が管理しているか知りたいのですが、shared_ptr の非侵入型設計によれば、shared_ptrtcp_connectionに関する情報を含めるべきではありません。ミッションインポッシブルです。

最後に、妥協enable_shared_from_thisして助けを求める必要があります。CRTP idiomenable_shared_from_thisを使用してクラスを派生させる必要があります。そのため、見た目はとてもトリッキーです。tcp_connection

class tcp_connection : boost::enable_shared_from_this<tcp_connection> {
    void start()
    {
        // ...
        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));
    }
};

enable_shared_from_thistemplate は、shared_ptr の参照カウントのようないくつかのフィールドを保持しshared_from_this、メンバー関数が の代わりに shared_ptr を使用したいフィールドを提供しますthis

于 2013-10-16T07:52:52.263 に答える