3

ポインターを使用するシングルトン クラスが既にいくつかあります。

class Logger
{
public:

    static Logger* Instance()
    {
        if (!m_pInstance) m_pInstance = new Logger;
        return m_pInstance;
    }
private:
    Logger();
    Logger(Logger const&);
    Logger& operator=(Logger const&);
    static Logger* m_pInstance;
};

しかし、参照を使用して、より簡単な方法があります。

class Logger
{
public:
    static Logger& Instance()
    {
        static Logger theLogger;
        return theLogger;
    }
private:
    Logger();
    Logger(Logger const&);
    Logger& operator=(Logger const&);
    ~Logger();
};

記事C++ Singleton design patternを読むと、2 番目の方法について警告されます。

[潜在的な落とし穴]: この形式のシングルトンは、オブジェクトの寿命のために問題を引き起こす可能性があります。1 つのシングルトンが別のシングルトン内でインスタンス化される場合、デストラクタの呼び出しシーケンスに注意する必要があります。

しかし、私はそれを理解することはできません。誰かがそれを避けるべき悪い使い方を教えてもらえますか?

4

1 に答える 1

7

実際、どちらのオプションにも問題があります。

典型的なシングルトン

class Logger
{
public:

    static Logger* Instance()
    {
        if (!m_pInstance) m_pInstance = new Logger;
        return m_pInstance;
    }
private:
    Logger();
    Logger(Logger const&);
    Logger& operator=(Logger const&);
    static Logger* m_pInstance;
};

このコードはスレッドセーフではないため、 への呼び出しがnew Logger(異なるスレッドで) 数回発生する可能性があります。またLogger、デストラクタを実行する必要がある場合に問題になる可能性があるインスタンスをリークします (インスタンスは削除されないため)。

マイヤーのシングルトン

class Logger
{
public:
    static Logger& Instance()
    {
        static Logger theLogger;
        return theLogger;
    }
private:
    Logger();
    Logger(Logger const&);
    Logger& operator=(Logger const&);
    ~Logger();
};

マイヤーのシングルトンの問題は、実際にはオブジェクトの破壊によるものです。main から戻ると、すべてのグローバルのデストラクタが、それらのグローバルが構築された順序と逆の順序で実行されます1。これはそれらのグローバルがスタック上に構築されたかのようです。

ロガーの前にオブジェクトが構築され、それを独自のデストラクタで使用しようとした場合。その後、すでに破棄されたオブジェクトが使用され、未定義の動作が発生します。

1より正確には、コンストラクターが完了した逆の順序で。


最も簡単な代替手段は、Typical Singletonを再検討することです:

class Logger {
public:
    static Logger& Instance() {
        static Logger* L = new Logger;
        return *L;
    }
private:
    ...
};

これはスレッドセーフになりましたが、それでもリークします。しかし、実際には、このオブジェクトがデストラクタが呼び出される他のどのオブジェクトよりも長く存続することが保証されるため、リークはここで望まれます。警告: デストラクタで何かを行う場合、実行されることはありません。

他にもバリエーションがあります。たとえば、Alexandrescu の Phoenix Singleton は、死後に必要になった場合に灰から復活します。ただし、破棄中にスレッドセーフと安全な動作の両方を実現することは困難です。

于 2013-03-09T16:27:32.753 に答える