3

シングルトンを作成しようとしたときに、これを思いつきました。例:(MySelfスレッドセーフで、ダブルチェックロックを使用しないシングルトンを作成しようとしています)

class MySelf
{
private:
    string Name;
    int Age;

    MySelf()
    {
        Name = "Deamonpog";
        Age = 24;
        cout << "Constructing MySelf : " << Name << endl;
    };

    friend class MySingleton;

public:
    ~MySelf(){ cout << "Destructing MySelf : " << Name << endl; };

    int MyAge() const
    {
        return Age;
    }
};

class MySingleton
{
private:
    static MySelf mself;
public:
    static MySelf * GetInstance()
    {
        return &mself;
    }
};

MySelf MySingleton::mself;

今、私はそれを次のように簡単に使用できます、

cout << "I am " << MySingleton::GetInstance()->MyAge() << endl;

私が作成しようとしているクラスは最初から最後までそこにあるので、私は怠惰な初期化を望んでいません。しかし、このスレッドは安全ですか?(私の知る限り、それは大丈夫のようです)

これで問題がなければ、このようなジェネリックプログラミングを使用しますか?

template <class T>
class GenericSingleton
{
private:
    static T _instance;

public:
    static T * GetInstance()
    {
        return &_instance;
    }
};

template <class T>
T GenericSingleton<T>::_instance;

だから私はこれを他のクラスでも使うことができます。必要なシグルトンに追加friend class GenericSingleton<MySelf>;する必要があります(たとえば、MySelfクラスに追加する必要があります)。

この実装は問題を引き起こす可能性がありますか?私は実際にライブラリを作成しています。一部のシングルトンはエクスポートされ、一部はエクスポートされません。また、これがライブラリではなく、別のアプリケーション用である場合はどうなりますか?

- 編集 -

だから今私はこのようにする必要があります(私はまだC ++11をサポートしていないVC++を使用しているので)、

static MySelf & GetInstance()
{
    WaitForMutex(mymutex); // some function from the threading library
    if( NULL == _instance )
    {
         _instance = new MySelf();
    }
    ReleaseMutex(mymutex); // release function of the same library
    Return _instance;
}

そして、関数を1回使用し、その後使用するためにキャッシュするようにユーザーに指示します。(または、関数の名前を変更して、Initialize()ロックや作成なしで参照を返すための別のメソッドを作成することもできます。)では、どこにあるmymutexべきですか?どこで初期化する必要がありますか?

4

2 に答える 2

6

いいえ、しかしそれは主要な問題ではありません。

グローバル オブジェクト ( などstatic) の初期化は、翻訳単位間で順不同です。つまり、呼び出しMySingleton::GetInstance()ていた 1 つのグローバルの作成中に、初期化されたメモリへのポインターになってしまう可能性があります。

Initialization Order Fiascoを参照してください。

さらに、この初期化フェーズ中に 2 番目のスレッドを起動すると、部分的に初期化されたオブジェクトにアクセスできてしまいます。

一般に、Meyer のシングルトンを使用することをお勧めします。

MySelf& MySelf::Instance() { static MySelf S; return S; }

これは、次の 2 つの方法で初期化順序の大失敗を回避します。

  • Instance()返されるときにオブジェクトが初期化されることが保証されています
  • C++11 以降、コンパイラは、初期化中の再入可能アクセスが検出されるように初期化コードを計測する必要があります (gcc は C++03 で既に実行されています)。

さらに、C++11 以降では、これはスレッド セーフである必要があります。つまりInstance()、オブジェクトの構築中に別のスレッドを呼び出すと、構築が終了するまで辛抱強く待機し、他のすべてのインスタンスと同じインスタンスを返します。スレッド (gcc は C++03 で既に行っていました)。

注:無関係なクラスを使用することは、付加価値のない単なる入力です。それを捨ててください。

于 2012-11-29T09:48:42.937 に答える
5

より簡単な実装は次のとおりです。

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

また、静的変数はスレッドの前に作成されるため、 C++11 ではスレッドセーフです。

于 2012-11-29T09:38:01.760 に答える