1

インクルード ガードについては既に知っていますが、把握したい問題がいくつかあります。

例 1

フー。

int SumOfNums(int i, int j);

Foo.cpp

#include "Foo.h"
int SumOfNums(int i, int j){
   return i+j;
}

main.cpp

#include "Foo.h"
#include "Foo.h"

int main(){
    SumOfNumbs(5,10);
}

これはコンパイルして正常に実行されます。

例 2

フー。

int SumOfNums(int i, int j);
int i;

Foo.cpp

#include "Foo.h"
int SumOfNums(int i, int j){
   return i+j;
}

main.cpp

#include "Foo.h"

int main(){
    SumOfNumbs(5,10);
}

コンパイラによる 'i' の再定義。

例 3

フー。

int SumOfNums(int i, int j);
enum FooBar{FOO, BAR};

Foo.cpp

#include "Foo.h"
int SumOfNums(int i, int j){
   return i+j;
}

main.cpp

#include "Foo.h"

int main(){
    SumOfNumbs(5,10);
}

これはコンパイルして正常に実行されます。

例 4

フー。

int SumOfNums(int i, int j);
enum FooBar{FOO, BAR};

Foo.cpp

#include "Foo.h"
int SumOfNums(int i, int j){
   return i+j;
}

main.cpp

#include "Foo.h"
#include "Foo.h"
int main(){
    SumOfNumbs(5,10);
}

コンパイラに合わせて FooBar を再定義。

要約すると:

例 1 - インクルード ガードがないのに、なぜ Foo.h を main.cpp に 2 回インクルードできるのでしょうか?

例 2 - int 変数と関数ヘッダーの違いは?

例 3 - Foo.cpp と main.cpp に FooBar の定義が 1 つずつあるのに、リンカが文句を言わないのはなぜですか?

例 4 - これと例 1の違いは何ですか?

4

5 に答える 5

4
  1. 関数を複数回宣言してもエラーにはなりません。

  2. の関数宣言は、どこかSumOfNumsに存在することをコンパイラに伝えるだけです (ただし、ここにはありません)。SumOfNumsの定義によりi、グローバル領域にストレージが割り当てられ、名前が付けられます。定義を含むヘッダーが複数回含まれているため、各ファイルに 1 つずつ、2 つの の定義があります。i.cpp

  3. リンカはenum FooBar. 列挙型の値は、コンパイラによって定数として使用されます。

  4. この例には の宣言が含まれていますが、enum FooBar例 1 には含まれていません。コンパイラは、指定されたenum1 回の宣言のみを想定しています。

于 2012-10-03T23:26:07.497 に答える
4

例 1 - インクルード ガードがないのに、なぜ Foo.h を main.cpp に 2 回インクルードできるのですか?

その場合、関数を宣言するだけだからです。必要な数の宣言を行うことができます。

例 2 - int 変数は関数ヘッダーとどう違うのですか?

これも定義であるため、1 つの定義ルールに違反します。

例 3 - Foo.cpp と main.cpp に FooBar の定義が 1 つずつあるのに、リンカが文句を言わないのはなぜですか?

翻訳単位全体で型を定義することは問題ありません。

Ex4 - これと Ex1 の違いは何ですか?

同じ翻訳単位で同じ型を 2 回定義しています - 許可されていません。

于 2012-10-03T23:29:18.743 に答える
3
  1. 関数宣言は繰り返すことができますが、型定義などは繰り返すことができないためです。
  2. ヘッダーを含む各翻訳単位で変数iを定義しますが、「1 つの定義規則」は、プログラム内の変数に対して 1 つの定義しか持てないことを意味します。
  3. の定義enum FooBarは純粋に型情報です。ヘッダーは、その型の変数を定義も宣言もしません。のような標準ヘッダーでも同様の動作が得られます<iostream>
  4. ここでの違いは、enum FooBar( Foo.h2 回インクルードすることによって) の型定義を繰り返そうとすることと、型の再定義が許可されていないことです。これが、ヘッダー ガードを使用する主な理由です。
于 2012-10-03T23:26:56.373 に答える
2

一般的な答えは、複数回宣言できますが、定義できるのは1回だけです(完全に正しいわけではありません。テンプレートのデフォルトの引数など、1回だけ宣言できるものはほとんどありません)。複数の定義が翻訳単位で見られる場合、コンパイラは文句を言います。物事の複数の定義が異なる翻訳単位で見られ、それらが異なる翻訳単位で複数回定義できない場合、リンカは文句を言います。たとえば、タイプ、テンプレート、およびインライン関数は、問題なく各変換ユニットで1回定義できます。通常の機能は、1つの翻訳単位でのみ定義できます。

最初の例では、ヘッダーで関数を宣言するだけです。関数は何度でも宣言できます。ただし、定義できるのは1回だけです。

2番目の例には、変数の定義が含まれていますi。このヘッダーをコンパイルする各翻訳単位には、の定義が含まれますi。リンカが物を構築しようとすると、の定義が2つあることを検出し、i失敗します。インクルードガードは、1つの翻訳ユニット内でのみ機能するため、この問題を防ぐことはできません。

3番目の例では、ヘッダーで関数を宣言し、。も定義していenumます。タイプは各変換ユニットで1回だけ定義できますが、複数の変換ユニットはすべてタイプの定義を持つことができます。理由は単純です。型はコードを作成せず、変数にスペースを割り当てません。

4番目の例には、2回定義するヘッダーが含まれていenumます。つまり、変換ユニットは型の再定義を確認して失敗します。インクルードガードを使用していた場合、この問題は検出されたはずです。

于 2012-10-03T23:33:00.423 に答える
0

例1 - できるからできる。つまり、合法です。言語には、ヘッダー ファイルを複数回インクルードすることを妨げるものは何もありません。もしあれば、そもそもガードを含める必要はありません。Foo.hこれには、2回含まれることを妨げるものは何もありません。含まれているのは関数プロトタイプのみであり、それらがすべて同じである限り、必要な数の関数プロトタイプのコピーを含めることができます。

2 -という名前のグローバル変数が 2 つあるため、リンカーから重複したシンボル メッセージが表示されますi。しかし、それはリンカーの問題であり、コンパイラの問題ではありません。実際にコンパイラ エラーが発生している場合は、 をインクルードしたFoo.cppFoo.h、グローバル変数 を持っていることが原因である可能性がありますi。ただし、の本体内では、関数パラメーターとしてSumOfNumsも使用しています。iしたがって、その関数内では、 global にアクセスできませんi。ただし、すべての C++ コンパイラがそれについて不満を言うわけではありません。ある人はあなたに警告を与え、コンパイルという陽気な仕事を続けます。

Ex3 -FooBar変数ではなく型だからです。リンカー シンボルは、グローバル変数と関数の実装 (単なるプロトタイプではなく、本体を持つ関数を意味します) に対してのみ生成されます。をインクルードするだけでリンカ シンボルが生成されFoo.hないため、リンカの問題はありません。

Ex4 -Foo.hこの例の は型を定義していますが、例 1 の は型を定義していません。したがって、Foo.h同じmain.cppファイルに 2 回含めることで、同じ型定義を 2 回注入することになります。それは違法です。それが型定義では違法であるのに、関数プロトタイプでは合法である理由を私に尋ねると、言語仕様はそのように書かれているということしかわかりません。何らかの理由があるのか​​もしれませんが、それが何かはわかりません。

于 2012-10-03T23:40:11.957 に答える