2011-01-28 29 views
3

C++プログラムでは、一度インスタンス化されるヘルパー定数オブジェクトが必要です。これらのオブジェクトはほとんど同じ翻訳単位内で使用されますので、これを実行する最も簡単な方法は、彼らが静的にするために、次のようになります。静的変数の代わりに使用するもの

static const Helper h(params); 

しかし、そこにこのstatic initialization order問題があるので、Helperは、いくつかの他の静力学(を参照している場合params経由)、これはUBにつながる可能性があります。

もう一つのポイントは、最終的にはこのオブジェクトを複数のユニット間で共有する必要があるということです。私がただstaticのままにして、.hファイルに入れると、それは複数のオブジェクトにつながります。私はexternなどで気にすることでそれを避けることができますが、これは最終的に同じ初期化の問題を引き起こす可能性があります(そしてC-ishのようには見えません)。

(例えばMySingleton::GetInstance().MyVar)私はシングルトンについて考えたが、それは定型的なコードと不便な構文にやり過ぎによるものだろう - これらのオブジェクトはヘルパーですので、彼らはそれらを複雑にしないように、物事を単純化することになっている...

同じC++よくある質問mentionsこのオプション:

Fred& x() 
{ 
    static Fred* ans = new Fred(); 
    return *ans; 
} 

がこれは本当に使用して良いことだと考えていますか?私はそれをこのようにしなければならないのでしょうか、あるいは他の選択肢を提案しますか?ありがとう。

編集:私は実際にそのヘルパーが必要な理由を明確にしておかなければなりません:通常の定数と非常によく似ていますが、あらかじめ計算されている可能性があります。私は自動的にマルチスレッドの問題を解決するので、mainの前にそれらをインスタンス化する方が好きです(C++ 03ではローカル統計は保護されていません)。また、私が言ったように、それらはしばしば翻訳単位に限定されるので、それらをエクスポートしてmain()で初期化するのは意味がありません。それらを単なる定数と考えることはできますが、実行時には分かります。

+0

良いことではないと思われる場合は、FAQには載っていません。だから、そのイディオムを使用したい場合は、先に進んでください。 – Philipp

+0

@Philipp:グローバルな状態は決して良いことではありません... @ 7vies:何とか漏れない限り、ヒープに 'ans'を割り当てる理由はありません。 –

+1

@Philipp:私が見ている限り、それは共同FAQではなく、著者の意見に完全に頼る理由はありません –

答えて

5

グローバル状態(変更可能かどうか)にはいくつかの可能性があります。

初期化の問題が発生する恐れがある場合は、local staticアプローチを使用してインスタンスを作成する必要があります。あなたが提示不格好シングルトンデザインは必須デザインではないことを

注:それは非グローバル設計への移行は非常に簡単になりますので、私はあまりそれを好むものの

class Singleton 
{ 
public: 
    static void DoSomething(int i) 
    { 
    Singleton& s = Instance(); 
    // do something with i 
    } 


private: 
    Singleton() {} 
    ~Singleton() {} 

    static Singleton& Instance() 
    { 
    static Singleton S; // no dynamic allocation, it's unnecessary 
    return S; 
    } 
}; 

// Invocation 
Singleton::DoSomething(i); 

別の設計では、やや似ています。

class Monoid 
{ 
public: 
    Monoid() 
    { 
    static State S; 
    state = &s; 
    } 

    void doSomething(int i) 
    { 
    state->count += i; 
    } 

private: 
    struct State 
    { 
    int count; 
    }; 

    State* state; 
}; 


// Use 
Monoid m; 
m.doSomething(1); 

ここでネット利点は国家の「グローバルらし」が隠されているということです、それはクライアントが心配する必要はないことを、実装の詳細です。キャッシュに非常に便利です。

私たちは、あなたは、デザインを疑問視されますしてみましょう:

  • あなたが実際に特異点を施行する必要がありますか?
  • mainが起動する前にオブジェクトを実際に作成する必要がありますか?

特異点は一般に強調されています。 C++ 0xはここで助けますが、それでもプログラマーに頼るのではなく、技術的に特異性を強制することは、テストを書くときのように非常に面倒です。実際には、各ユニットテストの間にプログラムをアンロード/リロードしますか?それぞれの設定を変更するだけですか?ああ。一度それをインスタンス化し、あなたの仲間のプログラマーや機能テストを信じている方がずっと簡単です)

2番目の質問は機能的ではなく技術的です。プログラムのエントリーポイントの前に設定が必要な場合は、起動時に簡単に読むことができます。

実際には、ライブラリのロード中にコンピューティングに1つの問題があります。エラーをどのように処理しますか?スローした場合、ライブラリはロードされません。あなたが投げていないと、あなたは無効な状態です。あまり面白くないですね。通常の制御フローロジックを使用することができるため、実際の作業が開始されると、作業がはるかに簡単になります。

あなたが状態が有効かどうかをテストすることを考えたら、テストする場所にすべてを構築するのはなぜですか?

最後に、globalの問題は、導入されている隠された依存関係です。依存関係が実行の流れやリファクタリングの影響についての理由を暗示している場合は、はるかに優れています。


EDIT

初期化順序の問題について:単一の翻訳単位内のオブジェクトは、それらが定義されている順序で初期化されることが保証されています。

したがって、次のコードは、標準に従って有効である:別の変換単位で定義された定数/変数を参照する場合

static int foo() { return std::numeric_limits<int>::max()/2; } 
static int bar(int c) { return c*2; } 

static int const x = foo(); 
static int const y = bar(x); 

初期化順序が唯一の問題です。したがって、staticオブジェクトは、同じ翻訳単位内のオブジェクトのみを参照する限り、問題なく自然に表現できます。

スペースの問題について:as-ifルールはここで不思議に思うことができます。正式には、as-ifルールは、ビヘイビアを指定し、それを提供するためにコンパイラ/リンカ/ランタイムに任せることを意味します。これが実際に最適化を可能にします。

したがって、定数のアドレスが決して取られないことをコンパイラチェーンが推論できる場合、定数を完全に削除する可能性があります。複数の定数が常に等しいと推測できる場合は、そのアドレスが決して検査されないということを再度推測することができます。

+0

そのような詳細な回答ありがとう!モノイドのトリックは本当に面白いです。しかし、私が必要とするものは非常に単なる定数です(詳細は質問を更新しましたのでご覧ください)。しかし、シングルトンはおそらく私の場合は良い選択肢ではありません。 。したがって、ほとんどの場合、グローバルアクセスは必要ありません。特異性は絶対に必要なわけではありませんが、私の定数を各翻訳単位に複製しないほうが最適です(メモリを消費します)。レイジーオブジェクトもオプションですが、次にマルチスレッド問題を回避する必要があります。 –

+0

@Matthieu:エラー処理も良い点ですが、これについて考える必要があります。 –

+0

@ 7vies:C++ 0xでは、ローカル・スタティックはマルチスレッドの存在下でも一度初期化されることが保証されています。一般的に、gcc(とClangは私が思う)は、標準がそのような保証をしていないとしても、すでに動作していました。 –

0

シングルトンとグローバルオブジェクトは、しばしば悪とみなされます。最も簡単かつ柔軟な方法は、あなたのmain関数内でオブジェクトのインスタンスを作成し、他の関数にこのオブジェクトを渡すことです:

void doSomething(const Helper& h); 
int main() { 
    const Parameters params(...); 
    const Helper h(params); 
    doSomething(h); 
} 

もう一つの方法は、ヘルパー関数非会員を作ることです。たぶんステートはまったく必要なく、ステートフルなオブジェクトを呼び出すときにステートフルなオブジェクトを渡すことができます。

FAQに記載されているローカル静的イディオムについては何も言えません。これは単純でスレッドセーフでなければならず、オブジェクトが変更可能でない場合は、簡単にモック可能で、離れた場所でアクションを導入する必要もありません。

+0

グローバル**可変**状態のため、それらは悪とみなされます。私は不変のオブジェクトについて話しています。繰り返しますが、ヘルパーをパラメータとして渡すと、複雑化することはありませんが、単純化することはありません。 –

+3

グローバルとsigletonについては何も悪いことはありません。動的初期設定がどのように機能するのか、その順序を強制する方法を知っているだけです。どのアプリケーションでも、1つのインスタンスに存在し、グローバルにアクセス可能でなければならないものがあります。 –

+1

@マキシム:これについてはすでに*たくさんの議論がありますが、もう一度やり直してください:)副作用などのために、グローバルな状態*は悪と見なされますが、それは全く異なる議論です! –

2

はい、Construct On First Useイディオムを使用すると、問題を簡略化できます。 が常により良いであるグローバルオブジェクトよりも、が他のグローバルオブジェクト上にあるに依存します。

もう1つの代替はSingleton Patternです。両方とも同様の問題を解決することができます。しかし、状況に適したものを決定し、要件を満たす必要があります。

私の知る限りでは、これらの2つのアプリケーションよりも「良い」ものはありません。

+0

これは答えかもしれません...もし私がすでに言ったことに何かを加えたら、\または私はそれを "他に何もない、これらの2つのうちの1つを使う"と考えるべきですか? –

+0

@ 7vies:私の知る限りでは、これらの2つよりも「良い」ものはありません。 – Nawaz

+0

@Nawaz:しかし、このコメントはあなたの答えよりも私には良い答えのように見えます:) –

0

Helperは、mainが実行される前に存在する必要がありますか?そうでない場合は、グローバルポインタ変数0に初期化します。その後、メインを使用して一定の状態で確定的な順序で入力します。あなたが好きなら、あなたのために逆参照をするヘルパー関数を作ることさえできます。

+0

確かに、それは明らかではありませんでした...私は詳細をより詳細に、質問を更新しました。 –

関連する問題