2013-02-25 9 views
14

私の具体的な質問は、C++でsingleton classを実装する場合、そこにあるということであるパフォーマンス、サイドの問題や何かについて、コードの下の2つの間のいずれかの実質的な違い:ヒープ/

class singleton 
{ 
    // ... 
    static singleton& getInstance() 
    { 
     // allocating on heap 
     static singleton* pInstance = new singleton(); 
     return *pInstance; 
    } 
    // ... 
}; 

と、この:

class singleton 
{ 
    // ... 
    static singleton& getInstance() 
    { 
     // using static variable 
     static singleton instance; 
     return instance; 
    } 
    // ... 
}; 


(生成された余分なマシン・コードがない私の知る限りとして、ヒープベースの実装では逆参照すると、パフォーマンスに影響を与えるべきではないことに注意してください逆参照のために。 。私はここでそれらを要約してみてください興味深い回答やコメントを持っている

:それはポインタと区別するための構文の問題だけらしい)

UPDATEです。 (興味のある人のために推奨されている詳細な回答を読む。):動的割り振り場合には、あなたが持っているのに対し、

シングルトンで
  • 静的ローカル変数を使用して、クラスデストラクタは、プロセス終了時に自動的に起動されオブジェクトの破壊をいつか管理します。
static singleton& getInstance() { 
     static std::auto_ptr<singleton> instance (new singleton()); 
     return *instance.get(); 
    } 
  • 動的割り当てを使用して、シングルトンが後者の場合のように、シングルトンオブジェクトに必要なメモリは(常に、静的シングルトン変数より「lazier」ある:スマートポインタを使用して?)プロセス起動時に(プログラムのロードに必要なメモリ全体の一部として)予約されており、シングルトンコンストラクタの呼び出しだけがgetInstance()コール時に延期されます。これは、sizeof(singleton)が大きいときに問題になることがあります。

  • どちらもC++ 11ではスレッドセーフです。しかし、以前のバージョンのC++では、実装固有のものでした。

  • 動的割り当てのケースでは、シングルトンオブジェクトにアクセスするために1レベルの間接参照を使用しますが、スタティックシングルトンオブジェクトの場合は、オブジェクトの直接アドレスが決定され、コンパイル時にハードコードされます。


P.S:私は@のTonyDの回答によると、元の投稿に使用されるのだ用語を修正しました。

+0

生成された2つのアセンブリを比較しましたか? –

+0

いいえ.2つの異なる実装で生成されたアセンブリを意味する場合は、明らかに異なります。ヒープに割り当てられ、ロード/呼び出し時間の初期化が行われるためです。逆参照のための生成されたアセンブリを意味する場合、いいえ、私は比較していません。私はちょうどそう思います。 –

答えて

7
  • 非ポインタバージョンは、コンパイル時に割り当てられたメモリを有している(ただし、両方が同じ構成を行う必要があり)、一方、明らかに、実行時にメモリを割り当てる必要がnewバージョンが

  • newバージョンは、プログラム終了時にオブジェクトのデストラクタを呼び出すことはありませんが、非newバージョンがします:あなたは、いくつかの静的/名前空間スコープのオブジェクトのことに注意する必要があり、この

    • を修正するために、スマートポインタを使用することができますデストラクタは、静的ローカルインスタンスのデストラクタが実行された後にシングルトンを起動しません。これについて懸念している場合は、シングルトンのライフタイムとその管理方法について少しお読みください。 Andrei AlexandrescuのModern C++ Designは非常に読みやすい処理をしています。
  • (C++ 03)では、いずれかがスレッドセーフであるかどうかは実装定義です。 (私はGCCがあると信じていますが、Visual Studioは評価を確認/修正するコメントはありません)。初期化中、同時実行は初期化の完了を待つ」 (sans recursion)。

ディスカッション再コンパイル時対あなたの概要といくつかのコメントを言葉で表現しまし方法から割り当て&初期

-時間を実行し、私はあなたが完全に微妙な側面を理解していない疑いがあります静的変数の割り当てと初期化の....

プログラムが3ローカルスタティック32ビットint S有する言う - abc - 異なる機能で:コンパイルrはOSローダーに3x32ビット= 12バイトのメモリを残すように指示するバイナリをコンパイルする可能性があります。コンパイラは、各変数がどのようなオフセットにあるかを決定します。aをデータセグメントのオフセット1000hexに、bを1004に、cを1008に設定します。プログラムが実行されると、OSローダーはメモリを割り当てる必要はありませんそれぞれ別々に知っているのは、合計で12バイトです。これは特に0で初期化するように頼まれているかもしれませんが、他のメモリの内容が残っていることをプロセスが確認できないようにしたいかもしれませんユーザーのプログラムプログラム内のマシンコード命令は、通常、a,bおよびcへのアクセスに対してオフセット1000,1004,1008をハードコードします。したがって、実行時にこれらのアドレスの割り当ては不要です。

  • 尖ったからメモリ(aの各々:

    動的メモリ割り当ては、その中にポインタが(たとえばp_ap_bp_c)さらに丁度記載したようにコンパイル時にアドレスを与え、しかしする異なります、bc)は実行時に見つけられる必要があります(通常、静的関数が最初に実行されたときに、コンパイラは他の答えについての私のコメントに従って早くそれを行うことができます)。

    • メモリカーレが小さすぎる場合ダイナミックアロケーションが成功するためにオペレーティングシステムによってプロセスに与えられていれば、プログラムライブラリはより多くのメモリをOSに要求する。 sbreak())を用い - OSは、典型的には、セキュリティ上の理由
    • ため一掃れるabcの各々のために割り当てられた動的アドレスがバックポインタp_ap_bp_cにコピーされなければなりません。

このダイナミックなアプローチは明らかに、より複雑です。

+0

良い点。 「コンパイル時のメモリ割り当て」とは、必要なメモリ空間がリンク時とロード時に予約されていることを意味しますが、初期化は関数呼び出し時に延期されますか? (そうでなければ最初の点は間違っているようだ) –

+0

私は[あなたの引用]に気づいたばかりです(http://stackoverflow.com/questions/15062767/heap-dynamic-vs-static-memory-allocation-for-c-singleton-class -instance/15063036#comment21179656_15062905)from C++ 11 6.7.4。しかし、C++ 03以前のバージョンはどうですか? –

+0

@MassoodKhaari:再割り当て:はい、統計情報のためのメモリ(必要量、セグメント、オフセット)の決定はコンパイル時に行われ、バイナリイメージはOSローダのために十分なことを示します(たとえばメモリ領域の合計サイズ)。メモリを脇に置いてください。私の答えによると、並行性は実装によって定義されています(もしあれば)... C++ 03 Standardにはスレッドはまったく言及されていなかったので、それらをサポートするかどうか、どうやってサポートするかは実装次第でした。 –

2

主な違いは、ローカルstaticを使用すると、プログラムを閉じるときにオブジェクトが破棄されることになります。ヒープ割り当てオブジェクトは破棄されずに破棄されます。

C++では、静的変数を関数内に宣言すると、最初にスコープを入力するときに初期化されます(プログラムの開始時ではなくグローバル静的持続時間変数の場合と同じように)。

一般に、プログラムの起動とシャットダウンは微妙な段階であり、デバッグするのが非常に難しいため、遅延初期化の使用から明示的な初期化に切り替えました。あなたのクラスが何か複雑なことをしておらず、失敗することができない場合(例:それはちょうどレジストリです)、怠け者の初期化もうまくいきます...そうでないと、制御が問題になることがあります。

mainの最初の命令を入力する前に、または最後の命令mainを実行した後にクラッシュするプログラムは、デバッグするのが難しいです。

シングルトンの遅延構築を使用するもう1つの問題は、コードがマルチスレッドの場合、同時スレッドで同時にシングルトンを初期化するリスクに注意する必要があることです。単一のスレッドコンテキストで初期化とシャットダウンを行う方が簡単です。

+0

はい、私はシンプルなオブジェクトにシングルトンも使用しています。しかし、それは興味深いようです。あなたは "明示的に制御された初期化"を実装するいくつかの方法について言及できますか? –

+0

そして破壊の問題を指摘してください。私はそれに気付かなかった。したがって、この場合、静的初期化がより良い選択と思われます。 –

+0

"初めてスコープを入力するときに初期化されます" - 時には/ C++ 11 6.7.4: "実装では、staticまたは のスレッド保存期間を使用して、他のブロックスコープ変数の早期初期化を実行できます実装が静的に名前空間スコープ(3.6.2)内の静的またはスレッド記憶期間を持つ変数 を初期化することが許可されている条件でなければ、その変数は で初期化され、その変数は初期化された 初期化が完了しました。 –