48

リンカーは、これに関する重複シンボルを報告します。

#ifndef testttt
#define testttt

void anything(){
    std::cout<<"hellooooooo";
}

#endif

これはインクルード ガードの内側にあるため、この関数は 1 回しか定義されないことが予想されます。しかし、どうやらそうではありません。

単語staticを前に置くことができ、それが機能することはわかっています( static は内部リンケージを提供することになっているため、まだ皮肉だと思いますが、関数は複数の cpp ファイルから使用できます)。

したがって、私の 2 つの部分からなる質問は次のとおりだと思います: 1) インクルード ガードは、他のヘッダー項目の場合のように、この関数の複数の定義を防止しないのはstaticなぜですか?他の翻訳単位で?これを追加すると、このヘッダー ファイルを含む任意の場所からこの関数を実際に呼び出すことができます。

4

3 に答える 3

56

「1) インクルード ガードが、他のヘッダー項目の場合のように、この関数の複数の定義を防止しないのはなぜですか」

各翻訳単位 (つまり .cpp ファイル) は個別に処理され、同じ条件を通過するためです。翻訳単位は、他の翻訳単位が検出したプリプロセッサ定義を共有しません。これは、そのヘッダーを処理するすべての翻訳単位に、その関数の定義が含まれることを意味します。もちろん、リンカーは、同じ関数に複数の定義があると文句を言います。

「2) static は他の翻訳単位で名前が表示されないようにするはずなのに、なぜ static という単語がこれを解決するのですか?」

staticキーワードは、翻訳単位ごとにその関数のプライベート コピーを作成するためです。

ただし、その関数を共有ヘッダーで定義する場合は、むしろそれを としてマークする必要がありinlineます。これにより、問題が解決され、プリプロセッサ ガードが不要になります。

于 2013-01-20T14:07:02.397 に答える
45

インクルード ガードが、他のヘッダー項目の場合のように、この関数の複数の定義を防止しないのはなぜですか?

C++ プログラムから実行可能ファイルを作成するプロセスは、次の 3 つの段階で構成されます。

  1. 前処理
  2. コンパイル&
  3. リンキング

前処理: マクロなどのプリプロセッサ ディレクティブは、この段階で置き換えられます。
コンパイルは、言語のセマンティクスをチェックして、ソース コードをオブジェクト コードに変換します。
リンクとは、生成されたすべてのオブジェクト コードをリンクして、実行可能ファイルを形成することです。

ヘッダー ガードは、前処理中にヘッダーの内容が同じ 翻訳単位に複数回含まれることを防ぎます。それらは、コンテンツが異なる翻訳単位に含まれることを妨げません。このヘッダー ファイルを異なる翻訳単位に含めると、これらの単位のそれぞれにこの関数の定義が含まれます。
コンパイラは、各翻訳単位を個別にコンパイルして個別のオブジェクト ファイル ( .o ) を生成します。これらの .o ファイルのそれぞれに、この関数定義のコピーが含まれます。リンカーが生成時に関数定義にリンクしようとすると.exe、同じ関数の複数の定義が見つかるため、どれにリンクするかについて混乱が生じます。この問題を回避するために、標準では、同じエンティティの複数の定義を禁止する1 つの定義規則 (ODR) 。
ご覧のとおり、関数定義をヘッダー ファイルに含め、そのヘッダー ファイルを複数の翻訳単位に含めると、ODR に違反します。
これを行う通常の方法は、ヘッダー ファイルに宣言を提供し、 1 つのソース ファイルに定義を提供することです。

static は他の翻訳単位で名前が表示されないようにするはずなのに、なぜ static という単語がこれを解決するのですか?

関数にキーワードstaticを追加すると、各翻訳単位には関数の独自のコピーが作成されます。デフォルトでは、関数には外部リンケージがありますが、関数に内部リンケージstaticを強制的に持たせます。したがって、定義は異なる翻訳単位からは見えません。このような関数の各インスタンスは個別の関数として扱われ (各関数のアドレスは異なります)、これらの関数の各インスタンスには、静的ローカル変数と文字列リテラルの独自のコピーがあります。これにより、実行可能ファイルのサイズが大幅に増加することに注意してください。


関数定義をヘッダー ファイルに含めたい場合。それには 3 つの方法があります。

  1. inline関数をまたはとしてマークします。
  2. static関数をまたはとしてマークします。
  3. 関数を名前のない名前空間に配置します。

上記の2番目の回答と同じように注意してください#1。 標準では、インライン関数の ODR が緩和され、各翻訳単位が独自の定義を持つことができます (すべての定義が同じ場合)。 #2
#3

したがって、本当に関数定義をヘッダーに入れたい場合#1は、それを行う正しい方法です。

于 2013-01-20T14:05:22.880 に答える
5

1) インクルード ガードが、他のヘッダー項目の場合のように、この関数の複数の定義を防止しないのはなぜですか?

インクルード ガードは、同じ翻訳単位にヘッダーが複数含まれないようにします。ただし、複数定義を防止するものではありませ。ヘッダーが複数の翻訳単位に含まれている場合、関数は各翻訳単位で定義されているため、複数定義エラーが発生し、外部リンケージがあるため、すべての翻訳単位が可能です。他のすべての翻訳単位の定義を参照してください。このエラーを回避するには、ヘッダーで宣言を提供し、1つのファイルで定義.cppを提供するだけで済みます。

One Definition Rule (ODR)External Linkageについてお読みください。

2) static は他の翻訳単位で名前が表示されないようにするはずなのに、なぜ static という単語がこれを解決するのですか?

static関数を各翻訳単位の内部にするためです。それが内部リンケージの意味です。他の翻訳単位は定義を見ることができません。

于 2013-01-20T14:07:51.090 に答える