私はいくつかのC++コードに取り組んでいて、しばらくの間私を悩ませてきた質問に遭遇しました...グローバルな静的コンストラクターとデストラクタがあるELFターゲットのLinuxホストでGCCをコンパイルしていると仮定しますと呼ばれる?
Crtbegin.oに関数_initがあり、crtend.oに関数_finiがあると聞きました。これらはcrt0.oによって呼び出されますか?または、ダイナミックリンカは実際にロードされたバイナリでそれらの存在を検出して呼び出しますか?もしそうなら、いつそれは実際にそれらを呼び出しますか?
コードがロードされ、実行され、実行時にアンロードされるときに、舞台裏で何が起こっているのかを理解できるように、私は主に知りたいと思っています。
前もって感謝します!
更新:私は基本的に、コンストラクターが呼び出される一般的な時間を把握しようとしています。この情報に基づいてコードで仮定を立てたくはありません。プログラムがロードされたときに、低レベルで何が起こっているのかをよりよく理解するためです。これはOS固有のものであることは理解していますが、この質問では少し絞り込んでみました。
非ローカル静的オブジェクトについて話すとき、多くの保証はありません。あなたがすでに知っているように(そしてそれはここでも言及されています)、それに依存するコードを書くべきではありません。静的初期化順序の大失敗...
静的オブジェクトは、静的初期化と動的初期化の2段階の初期化を経ます。前者が最初に発生し、ゼロ初期化または定数式による初期化を実行します。後者は、すべての静的初期化が完了した後に発生します。これは、たとえばコンストラクターが呼び出されるときです。
一般に、この初期化はmain()の前のある時点で発生します。ただし、多くの人が考えていることとは対照的に、それでもC++標準では保証されていません。実際に保証されているのは、初期化されるオブジェクトと同じ変換単位で定義された関数またはオブジェクトを使用する前に、初期化が行われることです。これはOS固有ではないことに注意してください。これはC++ルールです。標準からの引用は次のとおりです。
名前空間スコープのオブジェクトの動的初期化(8.5、9.4、12.1、12.6.1)が、mainの最初のステートメントの前に実行されるかどうかは、実装によって定義されます。 mainの最初のステートメントの後のある時点まで初期化が延期される場合、初期化されるオブジェクトと同じ変換単位で定義された関数またはオブジェクトが最初に使用される前に発生するものとします。
これはOS固有ではなく、コンパイラ固有です。
あなたは答えを与えました、初期化は___init
_で行われます。
2番目の部分では、gccで、変数定義に____attribute____((init_priority(PRIORITY)))
を付加して初期化の順序を保証できます。ここで、PRIORITY
は相対値であり、小さい数値が最初に初期化されます。
これは、コンパイラとランタイムに大きく依存します。グローバルオブジェクトが構築される時間を想定することはお勧めできません。
これは、すでに構築されている別のオブジェクトに依存する静的オブジェクトがある場合に特に問題になります。
これは「 静的初期化順序fiasco "」と呼ばれます。コードに当てはまらない場合でも、そのトピックに関するC++ Lite FAQの記事は読む価値があります。
あなたが持っている被付与者:
相互に依存関係のあるグローバルがある場合は、次の2つのオプションがあります。
class AType
{ AType() { log.report("A Constructed");}};
LogType log;
AType A;
// Or
Class AType()
{ AType() { getLog().report("A Constructed");}};
LogType& getLog()
{
static LogType log;
return log;
}
// Define A anywhere;
ここでは、オブジェクトログがオブジェクトBの前に破棄されないことを許可する必要があります。これは、ログがBの前に完全に構築される必要があることを意味します(破棄ルールの逆の順序が適用されるため)。ここでも同じ手法を使用できます。それらを同じ変換単位に配置するか、関数を使用してログを取得します。
class BType
{ ~BType() { log.report("B Destroyed");}};
LogType log;
BType B; // B constructed after log (so B will be destroyed first)
// Or
Class BType()
{ BType() { getLog();}
/*
* If log is used in the destructor then it must not be destroyed before B
* This means it must be constructed before B
* (reverse order destruction guarantees that it will then be destroyed after B)
*
* To achieve this just call the getLog() function in the constructor.
* This means that 'log' will be fully constructed before this object.
* This means it will be destroyed after and thus safe to use in the destructor.
*/
~BType() { getLog().report("B Destroyed");}
};
LogType& getLog()
{
static LogType log;
return log;
}
// Define B anywhere;