2

私のプロジェクトでは、スコット・マイヤーのやり方で作られた約4つのシングルトンを使用しています。それらの中の一つ:

LevelRenderer& LevelRenderer::Instance()
{
    static LevelRenderer obj;
    return obj;
}

今、それらのシングルトンのうちの2つでありLevelRendererLevelSymbolTable互いに相互作用します。たとえば、このメソッドでは次のようになります。

void LevelRenderer::Parse(std::vector<std::string>& lineSet)
{
    LevelSymbolTable& table = LevelSymbolTable::Instance();

    /** removed code which was irrelevant **/

    // for each line in lineSet
    BOOST_FOREACH(std::string line, lineSet)
    {
        // for each character in the line
        BOOST_FOREACH(char sym, line)
        {

            /** code... **/

            // otherwise
            else
            {
                sf::Sprite spr;

                // Used LevelSymbolTable's Instance here...
                table.GenerateSpriteFromSymbol(spr, sym);
                // ^ Inside LevelRenderer

                /** irrelevant code... **/
            }
        }
    }
}

さて、まだ問題は発生していませんが。私が恐れているのは、呼び出すLevelSymbolTableにインスタンスがすでに破棄されている場合はどうなるでしょうか。GenerateSpriteFromSymbol

スコットマイヤーの方法を使用したので、シングルトンのインスタンスはスタックによって割り当てられました。したがって、最後に作成された最初に破棄されたルールを使用して破棄されることが保証されます。さて、インスタンスがインスタンスの後にLevelSymbolTable作成された場合、インスタンスのに破棄されますよね?したがって、内部のメソッド(特にのデストラクタ)を呼び出すと、未定義の動作の土地を踏みます。 LevelRenderer LevelRendererLevelSymbolTableLevelRendererLevelRenderer

前に述べたように、この問題はデバッグ中に実際には発生しておらず、純粋に私の仮定と推測です。それで、私の結論は正しいですか?LevelSymbolTable前に破壊されやすいですLevelRenderer。もしそうなら、この混乱から抜け出す方法はありますか?

4

4 に答える 4

4

ここでは何も心配する必要はありません。*staticキーワードは、初期化されてからプログラムが終了するまで使用できることを保証します。したがって、静的変数は、初期化された後はいつでも呼び出すことができます。

また、ローカル変数ではなく、LevelSymbolTableへの参照があります。これは、クラス名の後のアンパサンドが意味するものです。したがって、ローカルで使用できますが、実際には、別の場所に存在する実際のオブジェクトを「参照」しています。したがって、メソッドが終了すると、参照はスコープ外になりますが、参照するオブジェクトは範囲外になりません。

*まあ、あなたは一つのことを心配しなければならないかもしれません。デストラクタでは、メモリやファイル参照、またはハンドルを持っているその他の性質のものをクリーンアップする必要があります。デストラクタで他のオブジェクトを呼び出す理由がわかりません。

于 2012-07-07T07:34:43.483 に答える
3

オブジェクト間の所有関係を定義します。LevelSymbolTableのメンバーとして持っているLevelRenderer

class LevelRenderer {
    LevelSymbolTable symbolTable;
public:
    static LevelRenderer& getInstance();
    ~LevelRenderer() { /* can use symbolTable here */ }
};

または、とLevelの両方を含む1つのシングルトンを作成します。SymbolTableRenderer

class Level {
    SymbolTable symbolTable;
    Renderer levelRenderer;   // note the order here
public:
    static Level& getInstance();

private:
    /* have LeverRenderer save reference to symbol table,
       now renderer can use symbol table anywhere */
    Level() : levelRenderer(symbolTable)
    { /* ... */ }
};

編集:または、シングルトンをすべて削除します。シングルトンが悪い理由をご覧ください。アプリケーションの構造はわかりませんが、私が見たところ、Levelそれ自体をレンダリングする方法を知っていて、そのシンボルテーブルを持っている通常のクラスとして持つことができます。そして、その存続期間を、アプリケーションで表すことになっているレベルに接続します。

于 2012-07-07T08:08:56.167 に答える
1

静的インスタンスは、プログラムの最初(メインの前)に作成され、最後(メインの後)にクリーンアップされます。これらのインスタンスがクリーンアップされる特定の順序に依存することはできません。つまり、2つのインスタンスがある場合(簡単にするために、それらをグローバルにします)

class one {
  one() {}
  ~one() {}
};

class two {
  two() {}
  ~two() {}
};

one the_one;
two the_other;

int main() {
  ...
  return 0;
}

the_oneのコンストラクタまたはデストラクタでアクティブであると仮定することはできませんし、そうすべきではありませんthe_other。(およびその逆。)

ただし、他のメンバー関数とmain自体の両方でアクティブになっていることを信頼できます。

于 2012-07-07T07:41:21.887 に答える
1

Parseプログラムがまだアクティブな間に呼び出されている可能性があるため、質問で提起したシナリオが発生する可能性はほとんどありません。プログラムが終了しようとしているときにのみ、デストラクタが呼び出されます。

コメントでは、グローバルなデストラクタの相互依存性というわずかに異なる懸念を示しています。これは、グローバルコンテナに登録するグローバルオブジェクトがある場合に実際に発生する可能性があります。オブジェクトがコンテナから削除され、コンテナがオブジェクトを排出することを期待するかもしれません。

これに対処する1つの方法は、コンテナがそれに登録されているオブジェクトの所有権を取得できるようにすることです。つまり、グローバルコンテナに登録されるのは、Scott Meyerのシングルトンインスタンスではなく、動的に割り当てられたインスタンスです。次に、グローバルコンテナは、グローバルデストラクタが呼び出されたときに、登録されたアイテムのクリーンアップを担当します。

于 2012-07-07T07:43:23.993 に答える