Garbage Collection

塵も積もれば山

目次

Blog 利用状況

ニュース

C++とかC#とか数学ネタを投下していく予定です。

[その他のページ]
日々の四方山話を綴った日記出水の日記帳

書庫

日記カテゴリ

[C++]あちこちを転々と shared_ptr

前回の続きです。
[C++]楽々TCPサーバ

前回紹介したソースなのですが、その中でtcp_connectionについて解説します。

class tcp_connection
  : public boost::enable_shared_from_this<tcp_connection>{

  void start_receive(){
    socket_.async_receive(
      boost::asio::buffer(recv_buffer_), 
      boost::bind(&tcp_connection::handle_receive, shared_from_this(),
      boost::asio::placeholders::error, _2));
  }
};

前回作成したtcp_connectionをもうちょっと見てみましょう。

よく見ると、boost::enable_shared_from_this というクラスから派生しています。
そして、async_recieveの引数をよく見ると、shared_from_thisという関数が見えます。

class udp_server{

  void start_receive(){
    socket_.async_receive_from(
      boost::asio::buffer(recv_buffer_), remote_endpoint_,
      boost::bind(&udp_server::handle_receive, this,
        boost::asio::placeholders::error, _2));
  }
};

同じ部分をUDPサーバで見てみましょう。
shared_from_this()の部分はただのthisですね。
UDPとTCPの差…ではありません、実体の差です。

実体…つまりどこで変数が確保されているか、です。
udp_serverクラスはmain関数内で確保されています。
では、tcp_connectionクラスはどこでしょうか。

前回にちょこっと書いたとおり、tcp_connection::create()関数が呼ばれているところを探せばいいのです。
tcp_server::start_accept()にありますね。

classs tcp_connection{
  typedef boost::shared_ptr<tcp_connection> pointer;
};

class tcp_server{

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

tcp_connection::pointerってのはtcp_connectionをshared_ptrで包んだものです。
shared_ptrなので、この関数を抜けたときに開放され…ると困りますね。
handle_acceptに引数として渡されているので、handle_accept関数が終わるまでは生きています。

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

ここでは、もうnew_connectionを持ち続けてはいません。
この関数を抜けたところで、tcp_connectionは開放されそうです。

いや、おかしいです。
逆にいつまで生きていて欲しいか、を考えると、TCPの通信が切断されるまで生きてないと困ります。

そこで、最初のtcp_connection::start_acceptに戻ります。
thisの代わりにshared_from_thisを使っています。
つまり、自分自身のshared_ptrを待ち受けイベントに忍び込ますことで、
自分自身が開放されるのを防いでいるわけです。

そして、だれも次の待ち受けイベントを登録しなくなった次点でtcp_connectionは開放されます。

なお、shared_from_this(this)と書く代わりにboost::shared_ptr(this)ではだめなのか。
shared_ptrはすでに作られているshared_ptrを探してくるわけではありません。
つまり、ポインタを管理するクラスが2つ出来てしまい、二重管理になってしまいます。

ここで出てきたshared_from_thisを提供するクラスが、boost::enable_shared_from_thisです。
内部的に自分自身のweak_ptrを持っています。
で、必要に応じてweak_ptrをshared_ptrに変換して、渡してあげるわけです。

shared_ptrでなくweak_ptrの理由は、shared_ptrだと自分自身がもっているものを捨てないと
自分自身が解放されないという、一種の循環参照になっているためです。

ということで、shared_ptrをうまく使う事で管理するクラスを作ってないのに
必要なくなったときに解放されるというtcp_connectionクラスを作っています。
もう、なんというか無茶苦茶ですね。

投稿日時 : 2010年10月28日 11:20

Feedback

# WwSZdSgyMKKiFLeIM 2012/01/07 7:28 http://www.luckyvitamin.com/c-1786-thorne-research

Sent the first post, but it wasn`t published. I am writing the second. It's me, the African tourist.

タイトル
名前
Url
コメント