インクルード ガードが、他のヘッダー項目の場合のように、この関数の複数の定義を防止しないのはなぜですか?
C++ プログラムから実行可能ファイルを作成するプロセスは、次の 3 つの段階で構成されます。
- 前処理
- コンパイル&
- リンキング
前処理: マクロなどのプリプロセッサ ディレクティブは、この段階で置き換えられます。
コンパイルは、言語のセマンティクスをチェックして、ソース コードをオブジェクト コードに変換します。
リンクとは、生成されたすべてのオブジェクト コードをリンクして、実行可能ファイルを形成することです。
ヘッダー ガードは、前処理中にヘッダーの内容が同じ 翻訳単位に複数回含まれることを防ぎます。それらは、コンテンツが異なる翻訳単位に含まれることを妨げません。このヘッダー ファイルを異なる翻訳単位に含めると、これらの単位のそれぞれにこの関数の定義が含まれます。
コンパイラは、各翻訳単位を個別にコンパイルして個別のオブジェクト ファイル ( .o ) を生成します。これらの .o ファイルのそれぞれに、この関数定義のコピーが含まれます。リンカーが生成時に関数定義にリンクしようとすると.exe
、同じ関数の複数の定義が見つかるため、どれにリンクするかについて混乱が生じます。この問題を回避するために、標準では、同じエンティティの複数の定義を禁止する1 つの定義規則 (ODR) 。
ご覧のとおり、関数定義をヘッダー ファイルに含め、そのヘッダー ファイルを複数の翻訳単位に含めると、ODR に違反します。
これを行う通常の方法は、ヘッダー ファイルに宣言を提供し、 1 つのソース ファイルに定義を提供することです。
static は他の翻訳単位で名前が表示されないようにするはずなのに、なぜ static という単語がこれを解決するのですか?
関数にキーワードstatic
を追加すると、各翻訳単位には関数の独自のコピーが作成されます。デフォルトでは、関数には外部リンケージがありますが、関数に内部リンケージstatic
を強制的に持たせます。したがって、定義は異なる翻訳単位からは見えません。このような関数の各インスタンスは個別の関数として扱われ (各関数のアドレスは異なります)、これらの関数の各インスタンスには、静的ローカル変数と文字列リテラルの独自のコピーがあります。これにより、実行可能ファイルのサイズが大幅に増加することに注意してください。
関数定義をヘッダー ファイルに含めたい場合。それには 3 つの方法があります。
inline
関数をまたはとしてマークします。
static
関数をまたはとしてマークします。
- 関数を名前のない名前空間に配置します。
上記の2番目の回答と同じように注意してください#1
。
標準では、インライン関数の ODR が緩和され、各翻訳単位が独自の定義を持つことができます (すべての定義が同じ場合)。 #2
#3
したがって、本当に関数定義をヘッダーに入れたい場合#1
は、それを行う正しい方法です。