3

次のコードは、シングルトン パターンの私の実装です。

 #include <iostream>

template<class T>
class Uncopyable
{
protected:
    Uncopyable(){}
    ~Uncopyable(){}
private:
    Uncopyable(const Uncopyable<T>&);
    Uncopyable& operator=(const Uncopyable<T>&);
};

template <class T>
class Singleton : private Uncopyable<T>
{
public:
    static T* getInstancePtr()
    {
        return instance;
    }
protected:
    Singleton<T>()
    {
        if(instance == 0)
        {
            instance = new T();
        }
    };
    ~Singleton<T>()
    {

    };
private:
    static T* instance;
};
template<class T> T* Singleton<T>::instance = 0;

class Test : public Singleton<Test>
{
public:
    Test(){};
    ~Test(){};
    inline void test() const
    {
        std::cout << "Blah" << std::endl;
    }
private:
    friend class Singleton<Test>;
protected:
};

int main(int argc, char* argv[])
{
    Test* t = Test::getInstancePtr();
    Test* t2 = Test::getInstancePtr();

    t->test();
    t2->test();

    return 0;
}

この形式で動作しますが、シングルトンのコンストラクタとデストラクタが非公開ではなく保護されているため、本当に正しいかどうかはわかりません。それらをプライベートとして宣言すると、クラスにアクセスできないため、コードはコンパイルされません。この実装は安全に使用できますか、または 1 つのインスタンスのみが作成および使用されるように改善するためにできることはありますか?

ありがとう

4

6 に答える 6

7

これは間違いなく、シングルトンの不適切な実装です。その実装には問題が多すぎます。

C++11 では、シングルトン パターンを使用std::call_onceして実装できます。std::once_flag以下に一例を示します。

//CRTP base singleton class

template<typename TDerived>
class Singleton 
{
    static std::unique_ptr<TDerived> m_instance;
    static std::once_flag            m_once;

protected:     

    Singleton() {}

public:

    ~Singleton() { }

    static TDerived & GetInstance() 
    {
        std::call_once
        ( 
           Singleton::m_once, 
           [] (){ Singleton::m_instance.reset( new TDerived() ); }
        );
        return *m_instance;
    }
};

template<typename TDerived> 
std::unique_ptr<TDerived>  Singleton<TDerived>::m_instance;

template<typename TDerived> 
std::once_flag   Singleton<TDerived>::m_once;

これで、次のように派生できます。

class Demo : public Singleton<Demo>
{
     public:
          void HelloWorld() { std::cout << "HelloWorld" << std::endl; }
};

//call HelloWorld() function through singleton instance!
DemoSingleton::GetInstance().HelloWorld();
于 2012-11-15T13:32:34.270 に答える
2

投稿したコードにはいくつか問題があります。

  1. Uncopyableクラスをテンプレート化する必要はありません
  2. Singletonクラスはスレッドセーフではありません
  3. Singletonインスタンスが削除されることはありません

アクセサーを次のように再実装します。

static T& GetInstance()
{
    static T instance;
    return instance;
}

次に、Singleton<T>::GetInstance()スレッド化の問題を回避するために、(初期化中に) アプリケーションのメイン スレッドで呼び出すようにしてください。

于 2012-11-15T13:29:09.687 に答える
1

いいえ、これはシングルトンパターンの適切な実装ではありません。機能しません。この例のTestの唯一のインスタンスはNULLです!コンストラクターが呼び出されることはありません!

Singleton::getInstancePtrを次のように変更する必要があります。

public:
    static T* getInstancePtr()
    {
        if(instance == 0)
        {
            instance = new T();
        }
        return instance;
    }
protected:
   Singleton<T>() {};

Testのコンストラクターが呼び出されます。

于 2012-11-15T13:41:22.937 に答える
1

あなたのデストラクタプライベートはコンパイルエラーを引き起こしますか?プロセスが終了すると、コンパイルはプライベート関数を呼び出すことができないため、オブジェクトを削除できません

于 2012-11-15T13:33:45.147 に答える
0

C++ には Singleton アンチパターンの正しい実装はありません。

この試みの主な問題は次のとおりです。

  • セマンティクスは非常に奇妙でエラーが発生しやすいものです。Singletonインスタンスを作成するには、どこかでインスタンス化する必要があります。あなたの例はインスタンスを作成せずt->test()、null ポインターを介して誤って関数を呼び出しています。
  • 構築はスレッドセーフではありません。2 つの非同期スレッドが両方ともインスタンス化する場合、2 つのインスタンスが作成される可能性がありますSingleton
  • インスタンスが実際に作成されると、リークされます。

誤りの少ない実装は、次のようになります。

template <typename T>
T & singleton()
{
    static T instance;
    return instance;
}

ただし、これにはまだ問題があります。特に、インスタンスが他の静的オブジェクトの前に破棄される可能性があり、デストラクタでインスタンスにアクセスしようとする可能性があります。

于 2012-11-15T13:32:56.023 に答える
0

通常、シングルトンオブジェクトはプログラムの存続期間中存続するため、そのように実装しません。動的割り当てを使用し、誰かがそれを解放し、シングルトンオブジェクトへのポインターを返す必要があるため、誤って削除する可能性があるため、私は次のようなものを使用します:

template< class T >
struct Singleton : Uncopyable<T> {
public:
    static T& get_instance() {
        static T res;
        use( res ); // make sure object initialized before used
        return res;
    }
private:
    static void use( T& ) {}
};
于 2012-11-15T13:27:24.367 に答える