11

dll AI には、テンプレート シングルトンがあります。

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

private:
  //All constructors are here
};

Dll BI でクラス Logger を定義します。Dll C、D、および E は Logger を使用し、次のようにアクセスします。

Singleton<Logger>::instance();

問題は、各 dll が独自のコピーをインスタンス化することです。

Singleton<Logger>.

同じシングルトン インスタンスを使用する代わりに。この問題の解決策は extern テンプレートを使用することだと理解しています。つまり、C、D、および E に含める必要がある dll です。

extern template class Singleton<Logger>;

dll B には以下が含まれている必要があります。

template class Singleton<Logger>;

これにより、複数のテンプレート インスタンスが作成されます。すべての dll に extern を入れようとしましたが、それでも機能しませんでした すべての dll から extern を削除しようとしましたが、それでも機能しませんでした。これは、テンプレート シングルトンを実装する標準的な方法ではありませんか? これを行う正しい方法は何ですか?

4

7 に答える 7

7

私にとってうまくいくトリックは__declspec(dllexport)、シングルトンのテンプレート定義に追加することです。テンプレートの実装をクラス定義から分割し、実装のみを A DLL に含めます。最後に、 を呼び出すダミー関数を作成して、A DLL でテンプレートを強制的にインスタンス化しますSingleton<Logger>::instance()

したがって、A DLL のヘッダー ファイルで、Singletonテンプレートを次のように定義します。

template <class T>
class __declspec(dllexport) Singleton {
public:
  static T &instance();
};

Singleton<Logger>次に、A DLL の cpp ファイルでテンプレートの実装を定義し、次のようなインスタンス化を強制します。

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

void instantiate_logger() {
  Singleton<Logger>::instance();
}

少なくとも私のコンパイラではinstantiate_logger、どこからでも呼び出す必要はありません。存在するだけで、コードが強制的に生成されます。したがって、この時点で A DLL のエクスポート テーブルをダンプすると、Singleton<Logger>::instance().

これで、C DLL と D DLL に のテンプレート定義を含むヘッダー ファイルを含めることができますが、Singletonテンプレートの実装がないため、コンパイラはそのテンプレートのコードを作成できません。これは、リンカーが の未解決の外部ファイルについて不平を言うことになることを意味しますがSingleton<Logger>::instance()、それを修正するには、A DLL のエクスポート ライブラリにリンクするだけで済みます。

要するに、 のコードSingleton<Logger>::instance()は DLL A にしか実装されていないため、複数のインスタンスを持つことはできません。

于 2013-07-19T16:09:57.270 に答える
7

The "correct" way to do this is...not to use a singleton.

If you want all other code to use the same instance of some type, then give that code a reference to that instance - as a parameter to a function or a constructor.

Using a singleton (non-template) would be exactly the same as using a global variable, a practice you should avoid.

Using a template means the compiler decides how to instantiate the code, and how to access the "instance". The problem you're experiencing is a combination of this and using a static in a DLL.

There are many reasons why singletons are bad, including lifetime issues (when, exactly, would it be safe to delete a singleton?), thread-safety issues, global shared access issues and more.

In summary, if you only want one instance of a thing, only create one instance of it, and pass it around to code that needs it.

于 2013-07-15T13:58:04.417 に答える
4

これは、構築できる可能性のある非常に大ざっぱなソリューションです。複数のテンプレートがインスタンス化されますが、それらはすべて同じインスタンス オブジェクトを共有します。

メモリ リークを回避するには、追加のコードが必要になります (たとえば、void * を shared_ptr の boost::any などに置き換えます)。

singleton.h 内

#if defined(DLL_EXPORTS)
    #define DLL_API __declspec(dllexport)
#else
    #define DLL_API __declspec(dllimport)
#endif

template <class T>
class Singleton
{
public:
  static T &instance()
  {
      T *instance = reinterpret_cast<T *>(details::getInstance(typeid(T)));
      if (instance == NULL)
      {
          instance = new T();
          details::setInstance(typeid(T), instance);
      }

      return *instance;
  }
};

namespace details
{

DLL_API void setInstance(const type_info &type, void *singleton);
DLL_API void *getInstance(const type_info &type);

}

singleton.cpp で。

#include <map>
#include <string>

namespace details
{

namespace
{

std::map<std::string, void *> singletons;

}

void setInstance(const type_info &type, void *singleton)
{
    singletons[type.name()] = singleton;
}

void *getInstance(const type_info &type)
{
    std::map<std::string, void *>::const_iterator iter = singletons.find(type.name());
    if (iter == singletons.end())
        return NULL;

    return iter->second;
}

}

私は今、より良い方法を考えることはできません。インスタンスは共通の場所に保存する必要があります。

于 2013-07-15T14:31:08.567 に答える
1

あなたの実装におけるあなたの問題だと思います:

static T _instance;

static 修飾子により、T クラスが dll ごとに 1 つインスタンス化されるコードがコンパイラによって作成されると思います。シングルトーンのさまざまな実装を試してください。Singletone クラスで static T フィールドを作成してみることができます。または、クラス内の静的ポインターを持つシングルトーンが機能するはずです。2番目のアプローチを使用することをお勧めします.B dllで指定します

Singletone<Logger>::instance = nullptr;

instance() の最初の呼び出しよりも、このポインターが初期化されます。そして、これで問題が解決すると思います。

PS。マルチスレッドのインスタンス化を手動で処理することを忘れないでください

于 2013-07-19T14:24:06.670 に答える
-2

のような条件を作ります

instance()
{
    if ( _instance == NULL ) {
    _instance = new Singleton();
    }

    return _instance;
}

これにより、単一のインスタンスのみが作成され、2 回目の呼び出しが行われると、古いインスタンスが返されます。

于 2013-07-12T11:51:29.797 に答える