3

概要: C++ でシングルトン ミックスインを作成するにはどうすればよいですか? 同じ関数、プライベート コンストラクターなどをコピーしget_instance()ないようにしていますが、これを mixin にする方法がわかりません。これは、静的インスタンスが mixin から継承するすべてのものによって共有されるためです。

各派生クラスをシングルトンにするのは簡単ですが、コードを複製せずにそれを行う方法はありますか? あなたの助けをありがとう、私は困惑しています。

コード:Registryオブジェクトを名前で検索するため のクラスを含むプログラムを作成しています。

#include <string>
#include <memory>
#include <map>
#include <string>
#include <assert.h>

template <typename T>
class Registry
{
private:
  // make private so that the class can't be instantiated and must be used via get_instance
  Registry() {}
protected:
  std::map<std::string, std::shared_ptr<T> > name_to_object_ptr;
public:
  static Registry<T> & get_instance()
  {
    static Registry<T> instance;
    return instance;
  }
  void register_name(const std::string & name, T*obj_ptr)
  {
    assert( name_to_object_ptr.count(name) == 0 );
    name_to_object_ptr[name] = std::shared_ptr<T>(obj_ptr);
  }
  const std::shared_ptr<T> & lookup_name(const std::string & name)
  {
    assert( name_to_object_ptr.count(name) > 0 );
    return name_to_object_ptr[name];
  }
  int size() const
  {
    return name_to_object_ptr.size();
  }
};

Registryのクラスはシングルトンです。シングルトンでなければなりません (登録されたオブジェクトが消えないようにするため)。

class DerivedRegistryA : public Registry<int>
{
};

class DerivedRegistryB : public Registry<int>
{
};

int main()
{
  DerivedRegistryA::get_instance().register_name(std::string("one"), new int(1));
  std::cout << DerivedRegistryA::get_instance().size() << std::endl;
  DerivedRegistryA::get_instance().register_name(std::string("two"), new int(2));
  std::cout << DerivedRegistryA::get_instance().size() << std::endl;
  DerivedRegistryA::get_instance().register_name(std::string("three"), new int(3));
  std::cout << DerivedRegistryA::get_instance().size() << std::endl;

  DerivedRegistryB::get_instance().register_name(std::string("four"), new int(4));
  std::cout << DerivedRegistryB::get_instance().size() << std::endl;

  return 0;
}

出力:

1
2
3
4

望ましい出力:

1
2
3
1

4

5 に答える 5

4

これはミックスインではありません。別のテンプレート パラメーターを宣言し、混合クラスを提供する必要があります。

template <typename T, typename Mixie>
class Registry
{
private:
  Registry() {}
protected:
  std::map<std::string, boost::shared_ptr<T> > name_to_object_ptr;
public:
  static Registry<T,Mixie> & get_instance()
  {
    static Registry<T,Mixie> instance;
    return instance;
  }
  ...
};
class DerivedRegistryA : public Registry<int,DerivedRegistryA>
{
};

class DerivedRegistryB : public Registry<int,DerivedRegistryB>
{
};
于 2012-04-19T04:42:28.780 に答える
3

ライオンベストの言ったことは正しいように聞こえます。これは、元のデザインにより近い関連するアイデアです。

オブジェクトのファクトリと同様の方法で機能するテンプレート クラスを宣言しRegistryます。私はそれを呼んだRegAccess

template <typename RegType>
class RegAccess
{
public:
  static RegType & get_instance()
  {
    static RegType instance;
    return instance;
  }
};

それを機能させるには、次のことを行います。

  • RegAccess<Registry<T> >の友人を宣言します(それを行うには、以前にRegistry<T>どこかで定義されていることを確認する必要があります) Registry<T>
  • Registryprivate ではなく、protectedのコンストラクターを作成します (派生クラスのコンストラクターが暗黙的に使用できるようにします)。
  • クラス定義get_instanceからメソッドを削除しますRegistry<T>

そして、メインプログラムは次のようになります。

int main()
{
  RegAccess<DerivedRegistryA>::get_instance().register_name(std::string("one"), new int(1));
  std::cout << RegAccess<DerivedRegistryA>::get_instance().size() << std::endl;
  RegAccess<DerivedRegistryA>::get_instance().register_name(std::string("two"), new int(2));
  std::cout << RegAccess<DerivedRegistryA>::get_instance().size() << std::endl;
  RegAccess<DerivedRegistryA>::get_instance().register_name(std::string("three"), new int(3));
  std::cout << RegAccess<DerivedRegistryA>::get_instance().size() << std::endl;

  RegAccess<DerivedRegistryB>::get_instance().register_name(std::string("four"), new int(4));
  std::cout << RegAccess<DerivedRegistryB>::get_instance().size() << std::endl;

  return 0;
}

これをテストしたところ、目的の出力が生成されました。

于 2012-04-19T04:51:39.273 に答える
2

必須の注意: Aaarrg a Singleton :x (*)

そうは言っても…

ステップ 1:型付きレジストリの作成。

template <typename T>
class Registry {
public:
    typedef std::string Key;
    typedef std::shared_ptr<T> ItemPtr;

    Registry() {}

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

    ItemPtr find(Key) const;

    void insert(Key, ItemPtr);

private:
    typedef std::map<Key, ItemPtr> StoreType;
    StoreType _store;
}; // class Registry

ステップ 2:タグ付きの Singleton 実装を作成します。

template <typename Object, typename>
class Singleton {
public:
    static Object& GetMutableInstance() {
        static Object O;
        return O;
    }

    static Object const& GetInstance() { return GetMutableInstance(); }

private:
    Singleton() = delete;
}; // class Singleton

注: タグ (2 番目のパラメーター) は、実装自体ではまったく役に立ちません。これは、同様の型のオブジェクトに対して異なるインスタンス (およびシングルトン) を作成できるようにするための方法にすぎません。デフォルトを指定しても問題ありません。

ステップ 3:楽しむ。

struct TypeA {};
typedef Singleton<Registry<int>, TypeA> RegistryA;

int main() {
    RegistryA::GetMutableInstance().insert("toto", std::make_shared(3));
}

(*)なぜシングルトンなのか?

正直なところ、ほとんどの場合、Singleton はほとんど役に立ちません。ああ、それは確かに物事をより簡単に見せます。最初に。

シングルトンは、グローバル変数の弱点(マルチスレッドの問題、テストの問題など)を単独で累積することができます。その上に、その一意性の強制 (より多くのテストの問題) と一般的により冗長なインターフェイス (地獄)により、別のくだらない層が追加されます。GetInstance

少なくとも一意性の強制を取り除き、通常のグローバル変数を提供する必要があります (昨日はVelociraptor Awareness Dayでした。まだ潜んでいる可能性があることに注意してください)。

もちろん、最善の道は、通常のオブジェクトだけを持ち、それを必要とする関数/メソッド/オブジェクトへの参照によって渡すことです。追跡しやすく、テストしやすい。

于 2012-04-19T06:45:45.613 に答える
2

問題は両方DerivedRegistryAでありDerivedRegistryB、同じものを共有していますname_to_object_ptr

get_instanceRegistry<T>どちらにも属さずDerivedRegistry、両方ともDerivedRegistry実際にはRegistry<int>同じ型です。たとえば、両方とも同じ型です。したがって、両方が同じstaticストレージを共有しています。静的ストレージはオブジェクトではなくクラスに属しているため。

したがって、どちらもタイプの同じコピーを取得していinstanceますRegistry<T>

派生クラスにインスタンス関数を含めるか、何らかの形ですべての派生クラスを異なる型として扱う必要があります。型を変更するがロジックを変更しないテンプレート引数を変更することで、いくつかの方法を実行できます。しかし、それは非常に悪い設計になります。

代わりにget_instancefromを削除できますRegistry

template <typename T>
class Singleton{
  public:
  static T& get_instance(){
    static T& instance;
    return instance;
  }
};

class DerivedRegistryA : public Registry<int>, public Singleton<DerivedRegistryA>{

};

Singletonこれは一般的な解決策であり、シングルトンが必要なすべてのクラスにクラスをプラグインできます

于 2012-04-19T04:35:34.897 に答える
2

これは問題の解決に役立つはずです。このシングルトンはスレッドセーフではないことに注意してください。ただし、必要に応じて変更できます。

詳細については、 CRTPを参照してください。

#include <iostream>
#include <map>
#include <string>

// simple singleton
template <class T>
class Singleton {
public:
    static T& Instance() { static T instance; return instance; }
protected:
    Singleton(){}
};

// your Registry Base
template <class T>
class Registry : public Singleton< Registry<T> > {
    friend class Singleton<Registry>;
public:
    void register_name( const std::string& name, T value ){ m_data[name] = value; }
    const T& lookup_name( const std::string& name ){ return m_data[name]; }

private:
    Registry(){}
    Registry(const Registry&){} // to prevent copies, you have to use ::Instance()

    std::map<std::string, T> m_data;
};

int main(int argc, char *argv[])
{
    Registry<int>& instance = Registry<int>::Instance();

    instance.register_name("Value1",1);
    Registry<int>::Instance().register_name("Value2",2);

    int value = instance.lookup_name("Value1");
    std::cout << "Value1=" << value << std::endl;
    std::cout << "Value2=" << Registry<int>::Instance().lookup_name("Value2") << std::endl;

    return 0;
}
于 2012-04-19T09:37:40.900 に答える