質問

C ++は、コンパイル単位(.cppファイル)の変数が宣言順に初期化されることを保証します。コンパイル単位の数については、このルールはそれぞれ個別に機能します(クラス外の静的変数を意味します)。

しかし、変数の初期化の順序は、異なるコンパイル単位間で未定義です。

g異なるOS)?

役に立ちましたか?

解決

おっしゃるように、順序は異なるコンパイル単位間では未定義です。

同じコンパイル単位内では、順序は明確に定義されています:定義と同じ順序。

これは、言語レベルではなくリンカーレベルで解決されるためです。そのため、リンカのドキュメントを確認する必要があります。私は本当にこれがどんな有用な方法でも役立つとは思いませんが。

gccの場合: ld

をご覧ください

リンクされているオブジェクトファイルの順序を変更しても、初期化の順序が変更されることがあります。ですから、あなたが心配する必要があるのはリンカだけではなく、ビルドシステムによってどのようにリンカが呼び出されるかです。問題を解決しようとしても、実際には初心者ではありません。

これは通常、独自の初期化中に相互に参照するグローバルを初期化するときにのみ問題になります(したがって、コンストラクターを持つオブジェクトにのみ影響します)。

問題を回避する手法があります。


  • 注1:グローバル:
    main()の前に潜在的に初期化される静的ストレージ期間変数を参照するために大まかに使用されます。
  • 注2:潜在的に
    一般的な場合、静的ストレージ期間変数はmainの前に初期化されることが期待されますが、コンパイラーは状況によっては初期化を延期できます(ルールは複雑です。詳細は標準を参照してください)。

他のヒント

モジュール間のコンストラクターの順序は、主にオブジェクトをリンカーに渡す順序の関数であると予想されます。

ただし、GCCでは init_priority を使用できます。グローバルアクターの順序を明示的に指定する

class Thingy
{
public:
    Thingy(char*p) {printf(p);}
};

Thingy a("A");
Thingy b("B");
Thingy c("C");

「ABC」は期待どおりに出力されますが、

Thingy a __attribute__((init_priority(300))) ("A");
Thingy b __attribute__((init_priority(200))) ("B");
Thingy c __attribute__((init_priority(400))) ("C");

「BAC」を出力します。

どうしても必要な場合を除き、この情報に頼るべきではないことは既にわかっているので、ここにきます。さまざまなツールチェーン(MSVC、gcc / ld、clang / llvmなど)での私の一般的な観察では、オブジェクトファイルがリンカーに渡される順序は、初期化される順序です。

これには例外があり、それらすべてを主張しているわけではありませんが、私が自分に出くわしたものは次のとおりです。

1)4.7より前のGCCバージョンは、実際にはリンク行の逆の順序で初期化されます。 GCCのこのチケットは、変更が発生したときであり、多くの問題が発生しました初期化順序に依存するプログラムのリスト(私のものも含む!)。

2)GCCおよびClangでは、 constructorの使用関数の優先順位は、初期化順序を変更できます。これは、「コンストラクタ」と宣言された関数にのみ適用されることに注意してください。 (つまり、グローバルオブジェクトコンストラクターと同じように実行する必要があります)。私はこのような優先順位を使用してみましたが、コンストラクター関数の優先順位が最も高い場合でも、優先順位のないすべてのコンストラクター(たとえば、通常のグローバルオブジェクト、優先順位のないコンストラクター関数)が first で初期化されることがわかりました。言い換えれば、優先順位は優先順位を持つ他の機能にのみ関連していますが、実際のファーストクラスの市民は優先順位のない人々です。さらに悪いことに、このルールは、上記のポイント(1)により、4.7より前のGCCでは事実上逆です。

3)Windowsには、 DllMain()。定義されている場合、パラメーター" fdwReason"で実行されます。すべてのグローバルデータが初期化された直後にDLL_PROCESS_ATTACHと等しくなり、 使用側アプリケーションがDLLの関数を呼び出す機会を持ちます。これは非常に便利な場合があり、GCCを使用する他のプラットフォームまたはCまたはC ++を使用するClangでは、これに類似した動作は絶対にありません 。最も近いのは、コンストラクター関数を優先的に作成することです(上記のポイント(2)を参照)。これはまったく同じものではなく、DllMain()が機能する多くのユースケースでは機能しません。

4)CMakeを使用してビルドシステムを生成している場合(多くの場合これを行います)、入力ソースファイルの順序は、リンカーに与えられた結果オブジェクトファイルの順序になることがわかりました。ただし、アプリケーション/ DLLが他のライブラリでもリンクしている場合がよくあります。その場合、これらのライブラリは入力ソースファイルのリンク行にあります。グローバルオブジェクトの1つを初期化する非常に最初のものにしたい場合は、幸運であり、そのオブジェクトを含むソースファイルをソースのリストの最初に置くことができますファイル。ただし、初期化する最後の1つを探している場合(これはDllMain()の動作を効果的に複製できます!)、その1つのソースファイルでadd_library()を呼び出すことができます静的ライブラリを生成し、結果の静的ライブラリを、アプリケーション/ DLLのtarget_link_libraries()呼び出しの最後のリンク依存関係として追加します。この場合、グローバルオブジェクトが最適化される可能性があることに注意してください。-whole-archive フラグを使用して、その特定の小さなアーカイブファイルの未使用のシンボルをリンカーが削除しないようにします。

終了のヒント

リンクされたアプリケーション/共有ライブラリの結果の初期化順序を完全に知るには、-print-mapをldリンカーに渡し、.init_arrayのgrep(またはGCC prioで)

http://www.parashift.com/c++ -faq-lite / ctors.html#faq-10.12 -このリンクは移動します。この one はより安定していますが、探し回る必要があります。

edit:osgxが提供するリンク

CのバックグラウンドからのMartinのコメントに加えて、静的変数は、プログラムの実行可能ファイルの一部であり、データセグメントに組み込まれ、割り当てられたスペースであると常に考えています。したがって、静的変数は、コードが実行される前に、プログラムのロード時に初期化されると考えることができます。これが発生する正確な順序は、リンカによって出力されるマップファイルのデータセグメントを確認することで確認できますが、ほとんどの意図と目的では、初期化は同時に行われます。

編集:静的オブジェクトの構築順序によっては、移植できない可能性が高いため、おそらく避けるべきです。

最終順序を本当に知りたい場合は、コンストラクタが現在のタイムスタンプを記録し、各cppファイルにクラスの静的インスタンスをいくつか作成して、初期化の最終順序を知ることができるようにすることをお勧めします。各ファイルに同じタイムスタンプが付けられないように、コンストラクタに少し時間のかかる操作を入れてください。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top