LoginSignup
1
0

More than 5 years have passed since last update.

アロケータを指定できて参照がなくなった時点で破棄されるSingleton

Posted at

アロケータを指定できて参照がなくなった時点で破棄されるSingleton

はじめに

shared_ptrを使用したSingletonの実装
Singletonのアロケータを後から指定 (shared_ptrを使用したSingletonの実装の続き)
上2つの続き的なものですが読まなくて大丈夫です。

実装

// アロケータ指定用
template <typename T>
struct singleton_allocator
{
  template <typename U>
  using type = std::allocator<U>;
};

// singleton
template <typename T>
class singleton
{
private:
  struct impl : public T{};
  using allocator_type = typename singleton_allocator<T>::template type<impl>;

protected:
  singleton() = default;

public:
  virtual ~singleton() { instance_.reset(); }

  singleton( singleton const& ) = delete;
  singleton& operator=( singleton const& ) = delete;

public:
  static std::shared_ptr<T> get_instance()
  {
    auto ret_ptr = instance_.lock();
    if( ret_ptr )
    {
      return ret_ptr;
    }

    ret_ptr   = std::allocate_shared<impl>( allocator_ );
    instance_ = std::weak_ptr<T>( ret_ptr );
    return ret_ptr;
  }

private:
  static std::weak_ptr<T> instance_;
  static allocator_type   allocator_;
}; // class singleton

template <typename T> std::weak_ptr<T> singleton<T>::instance_;
template <typename T>
typename singleton<T>::allocator_type singleton<T>::allocator_;

使い方

アロケータを指定しない場合

シングルトンにしたいクラスを singleton<T> を継承して定義します。

class A : public singleton<A>
{
private:
  friend class singleton<A>; // コンストラクタがprivateなのでfriend宣言が必要
  A(){}
public:
  ~A(){}

  void func(){}
};

使用例

int main()
{
  std::shared_ptr<A> a1 = A::get_instance(); // 初回のget_instance()時にインスタンス生成

  std::shared_ptr<A> a2 = A::get_instance(); // a1と同じオブジェクトを指す

  a1->func(); // shared_ptrなので普通のポインタのようにメンバにアクセスが可能
} // ここで参照が0になるので自動で破棄される

アロケータを指定する場合

使用するアロケータをmy_allocatorとします。
上のclass A定義する前に以下のような定義をします。

class A; // 前方宣言

template <>
struct pyrite::singleton_allocator<A>
{
  template <typename T>
  using type = my_allocator<T>;
};

後はアロケータを指定しない場合と同じです。

解説

template <typename T>
struct singleton_allocator
{
  template <typename U>
  using type = std::allocator<U>;
};

はじめのこの部分はアロケータを指定するのに必要な部分です。
singleton_allocator を部分特殊化することでアロケータを指定します。
デフォルトでは std::allocator を使用します。

型Tのコンストラクタがprivateで宣言されているはずなので allocate_shared<T>make_shared<T> が使えません。
しかし allocate_shared を使用したいので型Tを継承した impl を定義します。

struct impl : public T{};

この辺のことは std::make_shared から private コンストラクタを呼び出す にいろいろ書いてあるので是非。

デストラクタ内で instance_.reset() しているのは make_shared などで確保したメモリは弱参照が残っていても開放されないからです。

後は特に変わったことはしていないと思います。

まとめ

Singletonの型を変えずに派生クラスを定義する側でアロケータを指定できます。
Singletonのアロケータを指定したいと思うことがあるのかはわかりませんができないよりできたほうが良いだろうというだけです。

参照が0で get_instance() を呼び出すとインスタンスが生成されます。
参照が0になった時点で自動で破棄されます。

注意しなければならないのは

  • 実際確保する方が singleton<T>::impl 型である
  • T型に final をつけられない

あたりだと思います。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0