27

編集:申し訳ありませんが私の質問は明確ではありませんでした、なぜ本/記事は実装#2よりも実装#1を好むのですか?

シングルトンクラスの実装でポインタを使用することと静的オブジェクトを使用することの実際の利点は何ですか?ほとんどの本がこれを好むのはなぜですか

class Singleton
{
  private:

    static Singleton *p_inst;
    Singleton();

  public:

    static Singleton * instance()
    {
      if (!p_inst)
      {
        p_inst = new Singleton();
      }

      return p_inst;
    }
};

これ以上

class Singleton
{
  public:
    static Singleton& Instance()
    {
        static Singleton inst;
        return inst;
    }

  protected:
    Singleton(); // Prevent construction
    Singleton(const Singleton&); // Prevent construction by copying
    Singleton& operator=(const Singleton&); // Prevent assignment
    ~Singleton(); // Prevent unwanted destruction
};
4

7 に答える 7

19

なぜ本/記事は実装#2よりも実装#1を好むのですか?

シングルトンアンチパターンを説明しているほとんどの記事は、C ++で安全に実装しようとすると、隠れている危険性をすべて完全に理解しているわけではないためです。それを正しくするのは驚くほど難しいです。

シングルトンクラスの実装でポインタを使用することと静的オブジェクトを使用することの実際の利点は何ですか?

ポインタを使用しますnewが、を使用しないdeleteと、オブジェクトが破棄されないことが保証されるため、オブジェクトの存続期間が終了した後にオブジェクトにアクセスする危険はありません。初めてアクセスする「レイジー」作成と組み合わせると、すべてのアクセスが有効なオブジェクトへのアクセスであることが保証されます。欠点は、作成がスレッドセーフではないことと、オブジェクトとそれが取得するリソースがプログラムの最後に解放されないことです。

ローカル静的オブジェクトを使用すると、C++11スレッドモデルをサポートするすべてのコンパイラでスレッドセーフになります。また、オブジェクトはプログラムの終了時に破棄されます。ただし、オブジェクトの破棄後に(たとえば、別の静的オブジェクトのデストラクタから)オブジェクトにアクセスすることは可能であり、厄介なバグにつながる可能性があります。

最善のオプションは、静的データとグローバルにアクセス可能なデータをできるだけ避けることです。特に、シングルトンアンチパターンは絶対に使用しないでください。グローバルな静的データと、テストを不必要に困難にする奇妙なインスタンス化の制限を組み合わせています。

于 2012-10-24T11:04:33.620 に答える
9

2番目のバージョン(ローカル静的変数を使用)には、大きな利点があります。

フリーストアを使用する必要がないため、メモリリークとして検出されません。スレッドセーフです(in C++11)。短くてシンプルです。

唯一の欠点は、移植可能にスレッドセーフにすることが不可能であり(C ++ 11以前のコンパイラの場合)、シングルトンインスタンスを明示的に破棄するオプションがないことです。

于 2012-10-24T10:42:40.130 に答える
4

私は常に2番目の方が好きですが、最初の方にはいくつかの潜在的に興味深い利点があります。

  • 明快さ-ポインタがnullであるかどうかのチェックは、静的オブジェクトを構築するときにコンパイラが内部で行うことです。「学習」の観点からは、メソッドスコープで静的オブジェクトを使用したときに何が起こっているのかを理解することは有益です。

  • レイジーアロケーション-最初のケースでは、シングルトンオブジェクトはヒープアロケーションされます。関数が実行されない場合、オブジェクトは構築されず、メモリを消費しません。ただし、2番目のケースでは、「構築」が遅延している場合でも、プログラムが開始する前にオブジェクトを保持するためにリンカによってメモリが割り当てられます。

于 2012-10-24T10:55:14.187 に答える
3

2つ目は、非決定論的な破壊です。1つ目は、ポインタを削除する場合は、いつ削除するかを制御できます。

もちろん、最初の構成はスレッドセーフではありませんが、boost::call_once(またはstd::call_once利用可能な場合は)スレッドセーフにすることができます。

2番目の構造は十分に一般的であったため、技術的には標準ではなくても、多くのコンパイラがスレッドセーフにしました(標準ではオブジェクトは1回だけ作成する必要がありますが、完了時の標準の見方についてはよくわかりません。別のスレッドがそれを使用する前の構築)。

破棄の順序に問題がない場合は、コンパイラがスレッドセーフであると保証している限り、静的バージョンを使用できます。

于 2012-10-24T10:43:27.140 に答える
3

2番目の例は、「EffectiveC++」または「MoreEffectiveC ++」のいずれかで最初に公開されたため、「Meyers'Singleton」という名前で知られています。どちらかはわかりませんが、どちらも「デザインパターン」の後に出版されたため、Gang of Fourは、本が書かれたときに2番目のパターンを認識していなかった可能性があります。

また、最初のアプローチは他の言語でははるかに標準的です。最初のアプローチはJavaまたはC#で実行できますが、2番目のアプローチは実行できないため、さまざまなバックグラウンドを持つ人々が最初のアプローチをより有名にするもう1つの理由になる可能性があります。

技術的な面では、最初のアプローチでは、シングルトンがいつ破壊されるかを制御できますが、これは多くの頭痛の種になる可能性もあります。

于 2012-10-24T10:58:51.507 に答える
1

1つの利点は、シングルトンがすでにインスタンス化されているかどうかを確認する必要がないことです。

もう1つは、メモリの割り当てを解除することを心配する必要がないことです。

于 2012-10-24T10:42:43.500 に答える
-3

非ローカルスタティックはどうですか?誰かがこれに問題がありますか?

class Singleton
{
    static Singleton singleton;
    Singleton();
    // etc

public:
    static Singleton &Instance() { return singleton; }
};

Singleton Singleton::singleton;

// etc
于 2014-04-03T10:35:19.180 に答える