web-dev-qa-db-ja.com

C ++でお互いを含むヘッダー

私はC++初心者ですが、この(たぶん些細な)質問に対する答えをオンラインで見つけることができませんでした。 2つのクラスが互いに含まれているコードのコンパイルで問題が発生しています。はじめに、#includeステートメントをマクロの内側または外側に配置する必要がありますか?実際には、これは問題にならなかったようです。しかし、この特定の場合、私は問題を抱えています。 #includeステートメントをマクロの外に置くと、コンパイラーが再帰し、「#includeのネストが深すぎます」エラーが発生します。 #includeが呼び出される前にどちらのクラスも完全に定義されていないので、これは私には理にかなっているようです。しかし、奇妙なことに、それらを中に入れようとすると、クラスの1つのタイプを宣言できません。それが認識されないためです。ここに、本質的に、私がコンパイルしようとしているものがあります:

ああ

#ifndef A_H_
#define A_H_

#include "B.h"

class A
{
    private:
        B b;

    public:
        A() : b(*this) {}
};

#endif /*A_H_*/

B.h

#ifndef B_H_
#define B_H_

#include "A.h"

class B
{
    private:
            A& a;

    public:
        B(A& a) : a(a) {}
 };

#endif /*B_H_*/

main.cpp

#include "A.h"

int main()
{
    A a;
}

違いがある場合は、g ++ 4.3.2を使用しています。

そして明確にするために、一般的に、#includeステートメントはどこに行くべきですか?私は常にそれらがマクロの外に出ることを見てきましたが、私が説明したシナリオは明らかにこの原則を破っているようです。事前にヘルパーに感謝します!私がばかげた間違いをした場合、私の意図を明確にさせてください!

44
Scott

「マクロ」とは、#ifndefにガードが含まれていることを意味していると思いますか?もしそうなら、#includesは間違いなく内部に入ります。これは、ガードが存在する主な理由の1つです。そうしないと、気づいたように無限再帰が簡単に発生してしまいます。

とにかく、問題は、AクラスとBクラスを(他のクラス内で)使用するときに、まだ宣言されていないことです。 #includesが処理された後のコードの外観を見てください。

//#include "A.h" start
#ifndef A_H_
#define A_H_

//#include "B.h" start
#ifndef B_H_
#define B_H_

//#include "A.h" start
#ifndef A_H_ // A_H_ is already defined, so the contents of the file are skipped at this point
#endif /*A_H_*/

//#include "A.h" end

class B
{
    private:
            A& a;

    public:
            B(A& a) : a(a) {}
 };

#endif /*B_H_*/

//#include "B.h" end

class A
{
    private:
            B b;

    public:
            A() : b(*this) {}
};

#endif /*A_H_*/
//#include "A.h" end

int main()
{
    A a;
}

コードを読んでください。 Bはコンパイラーが最初に遭遇するクラスであり、A&メンバーが含まれています。 Aとは何ですか?コンパイラはAの定義をまだ検出していないため、エラーを発行します。

解決策は、Aの前方宣言を行うことです。Bの定義の前のある時点で、行class A;を追加します。

これにより、Aはクラスであるという必要な情報がコンパイラに提供されます。それについてはまだ何も知りませんが、Bはそれへの参照を含める必要があるだけなので、これで十分です。 Aの定義では、タイプB(参照ではない)のメンバーが必要なので、ここではBの定義全体を表示する必要があります。幸いなことに、それは。

67
jalf

そして明確にするために、一般的に、#includeステートメントはどこに行くべきですか?

あなたが言及した理由のために、内部の警備員。

他の問題の場合:クラスの少なくとも1つを前方宣言する必要があります。このような:

#ifndef B_H_
#define B_H_

// Instead of this:
//#include "A.h"

class A;

class B
{
    private:
            A& a;

    public:
            B(A& a) : a(a) {}
 };

#endif /*B_H_*/

ただし、これは宣言に対してのみ機能します。本当にseAのインスタンスになるとすぐに、それも定義する必要があります。

ところで、Nathanの言うことは真実です。クラスインスタンスを相互に再帰的に配置することはできません。これは、インスタンスへのpointers(または、あなたの場合は参照)でのみ機能します。

14
Konrad Rudolph

おっとっと! #includeステートメントをクラス内に置き、フォワード宣言を使用することを含むソリューションを見つけたと思います。したがって、コードは次のようになります。

#ifndef A_H_
#define A_H_

class B;

#include "B.h"

class A
{
    private:
            B b;

    public:
            A() : b(*this) {}
};

#endif /*A_H_*/

クラスBについても同様です。コンパイルされますが、これが最善の方法ですか?

4
Scott

このような状況では、前方宣言を使用してすべてのソースに含まれる共通ヘッダーを作成します。

#ifndef common_hpp
#define common_hpp

class A;
class B;

#endif

その場合、必要なのがそれらのクラスへのポインターまたは参照だけである場合、個々のクラスヘッダーファイルは通常、他のクラスを参照する#includeを必要としません。これらのヘッダーには他にも良い点はありますが、少なくとも循環参照に関する問題はcommon.hppで解決されます。

3
DarenW

これができるとは思えません。 2つのfunctionsを相互に再帰的に呼び出すことではなく、2つのobjectsを他の内部に再帰的に配置することを指します。家の写真を家の写真と一緒に置くことを考えてみてください。無限の数の家と写真があるので、これは無限のスペースを占有します。

あなたがcanすることは、ABのそれぞれに、相互へのポインタまたは参照のいずれかを含めることです。

0
Nathan Fellman

優れたソフトウェア設計における2つのクラス間の依存関係は、ツリーとして描画できます。

このため、C++では、2つの.hファイルが互いに#includeすることはできません。

0
Alex Spencer

一部のコンパイラ(gccを含む)も#pragma onceをサポートしていますが、質問に「ガードを含める」イディオムが通常の方法です。

0
frankodwyer