2021年9月22日水曜日

enable_shared_from_this と継承 忘備録

マルチスレッド・プログラミングで、継承されたオブジェクトの破棄タイミングが難しく、enable_shared_from_this を使う事にした。 ただ、共通インタフェイスを持たせたくて継承させたので、素のままの enable_shared_from_this では不味そうなので、正しく動作させるための用法を忘備録として書き留める事にした。
#include <iostream>
#include <cassert>
#include <memory>

class Base : public std::enable_shared_from_this<Base> {
protected:

  template <typename T>
  std::shared_ptr<T> shared_from(T* derived) {
    assert(this == derived);
    return std::static_pointer_cast<T>(shared_from_this());
  }

  int n_;
public:
  Base(int n) : n_(n) { std::cout << "Base()" << std::endl; }
  virtual ~Base() { std::cout << "~Base(" << n_ << ")" << std::endl; }
};

class Delived : public Base {
public:

  auto shared_from_this() { return shared_from(this); }


  Delived(int m) : Base(m) {
    std::cout << "Delived()" << std::endl;
  }
  
  virtual ~Delived() {
    std::cout << "~Delived()" << std::endl;
  }

};


void main() {

  std::shared_ptr<Base> test1;
  {
  {
    auto pbase = std::make_shared<Base>(1);
    auto pderived = std::make_shared<Delived>(2);

    test1 = pbase->shared_from_this();
    std::shared_ptr<Delived> test2 = pderived->shared_from_this();
  }
  std::cout << "end" << std::endl;
  }

}
実行すると
Base()     // pbase のコンストラクタ
Base()          // pderived の Base コンストラクタ
Delived()       // pderived のコンストラクタ
~Delived()      // スコープを抜けて test2が破棄され pbaseの参照カウントが0になり破棄
~Base(2)
end             // スコープを抜ける
~Base(1)        // test1が破棄され pderivedの参照カウントが0になり破棄