編集:申し訳ありませんが私の質問は明確ではありませんでした、なぜ本/記事は実装#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
};
本/記事が実装#2より実装#1を好むのはなぜですか?
シングルトンのアンチパターンを説明するほとんどの記事は、C++で安全に実装しようとするときに隠されたすべての危険を完全には理解していないためです。それを正しくするのは驚くほど難しいです。
シングルトンクラスの実装でポインターを使用することと、静的オブジェクトを使用することの実際の利点は何ですか?
new
を指定してdelete
を指定せずにポインタを使用すると、オブジェクトが破棄されないため、有効期間が終了した後にオブジェクトにアクセスする危険がありません。 「遅延」作成と組み合わせると、初めてアクセスされたときに、すべてのアクセスが有効なオブジェクトに対して行われることが保証されます。欠点は、作成がスレッドセーフではないこと、およびオブジェクトとそれが取得するリソースがプログラムの最後に解放されないことです。
ローカル静的オブジェクトを使用すると、C++ 11スレッドモデルをサポートするすべてのコンパイラーでスレッドセーフに作成できます。また、オブジェクトはプログラムの終了時に破棄されます。ただし、オブジェクトが破棄された後(たとえば、別の静的オブジェクトのデストラクタから)にアクセスすることは可能であり、厄介なバグにつながる可能性があります。
最善のオプションは、静的データとグローバルにアクセス可能なデータをできるだけ避けることです。特に、シングルトンアンチパターンは絶対に使用しないでください。グローバルな静的データと、テストを不必要に困難にする奇妙なインスタンス化制限を組み合わせています。
2番目のバージョン(ローカル静的変数を使用)には、大きな利点があります。
フリーストアを使用する必要がないため、メモリリークとして検出されません。スレッドセーフです(C++11
)。短くてシンプルです。
唯一の欠点は、(C++ 11以前のコンパイラの場合)移植性のあるスレッドセーフにすることは不可能であり、シングルトンインスタンスを明示的に破棄するオプションが提供されないことです。
私は常に 2番目を好みますが、最初にはいくつかの潜在的に興味深い利点があります:-
明快さ-ポインターがnullであることのチェックは、静的オブジェクトを構築するときにコンパイラーが内部で行うことです。 「学習」の観点からは、メソッドスコープで静的オブジェクトを使用したときに何が起こっているのかを理解することは有益です。
遅延割り当て-最初のケースでは、シングルトンオブジェクトはヒープに割り当てられます。関数が実行されない場合、オブジェクトは構築されず、メモリを消費しません。ただし、2番目のケースでは、「構築」が遅延している場合でも、プログラムが開始する前にオブジェクトを保持するためにリンカによってメモリが割り当てられます。
2つ目は非決定的な破壊です。 1つ目は、ポインタを削除する場合は、それを制御することです。
もちろん、最初の構成はスレッドセーフではありませんが、boost::call_once
(または利用可能な場合はstd::call_once
)を使用してスレッドセーフにすることができます。
2番目の構造は十分に一般的であったため、技術的には標準ではなくても、多くのコンパイラがスレッドセーフにしました(標準ではオブジェクトは1回だけ作成する必要がありますが、完了時の標準の見方についてはよくわかりません。別のスレッドがそれを使用する前の構築)。
破棄の順序に問題がない場合は、コンパイラがスレッドセーフであると保証している限り、静的バージョンを使用できます。
2番目の例は、「Meyers 'Singleton」という名前で知られています。これは、「Effective C++」または「More Effective C++」で最初に公開されたためです。どちらかはわかりませんが、どちらも「デザインパターン」の後に公開されたため、Gang of Fourは、本が書かれたときに2番目のパターンに気付いていなかった可能性があります。
また、最初のアプローチは他の言語でははるかに標準的です。最初のアプローチはJavaまたはC#で実行できますが、2番目のアプローチは実行できません。そのため、異なる背景から来た人々は、より有名になる最初のもの。
技術面では、最初のアプローチでシングルトンがいつ破壊されるかを制御できますが、これは多くの頭痛の種になる可能性もあります。
1つの利点は、シングルトンがすでにインスタンス化されているかどうかを確認する必要がないことです。
もう1つは、メモリの割り当て解除について心配する必要がないことです。