web-dev-qa-db-ja.com

相互に含まれるc ++ヘッダーファイル

別々のヘッダーファイルで定義された2つのクラスがあります。各ファイルには、他のクラスのタイプであるフィールドがあります。今、私は各ファイルのヘッダーに他のファイルのヘッダーを含めましたが、コンパイラーはエラーを生成しています。何が欠けていますか?

39
MegaManX

各クラスに「他のクラスのタイプであるフィールド」を持つことはできません。これは再帰的な定義であり、コンパイラーはそれを理解できないだけでなく、論理的にも意味を持ちません。

他のクラスの型であるフィールドを持つ各クラスは、M.Cでしか見られない不可能です。このようなエッシャーの図面、またはそのアニメーション:

based on Escher's "Print Gallery" Lithograph, 1956

ソース: escherdroste.math.leidenuniv.nl

エッシャーの「プリントギャラリー」リトグラフ、1956に基づく Wikipediaを参照

2つのフィールドの1つは、pointerである必要があります。これは、再帰的包含を破壊し、論理的な不可能性を回避するためです。

クラスBがクラスAのインスタンスを含む場合、明らかにAはクラスBの前に宣言する必要があるため、AがBのコンパイル時にコンパイラーに既に認識されている必要があります。しかし、クラスAがクラスBの前に宣言されていますが、AのBへのポインタをどのように宣言できますか?クラスBは、Aがコンパイルされた時点ではまだ知られていません!これに対する答えは、forward宣言として知られる特別な構成であり、このような状況に対応するために正確に存在します。クラスBの前方宣言は次のようになります。

class B;

コンパイラーに伝えるのは、Bというクラスがあるということだけです。クラスBの内容についてはコンパイラーに何も伝えないので、それでできることはほとんどありませんが、1つのことができます:Bへのポインタを宣言します。

したがって、問題の完全な解決策は次のようになります。

ファイル「A.h」:

/* This is called a "forward declaration".  We use it to tell the compiler that the 
   identifier "B" will from now on stand for a class, and this class will be defined 
   later.  We will not be able to make any use of "B" before it has been defined, but 
   we will at least be able to declare pointers to it. */
class B;

class A
{
    /* We cannot have a field of type "B" here, because it has not been defined yet. 
       However, with the forward declaration we have told the compiler that "B" is a 
       class, so we can at least have a field which is a pointer to "B". */
    B* pb; 
}

ファイル「B.h」:

#include "A.h"

class B
{
   /* the compiler now knows the size of "A", so we can have a field of type "A". */
   A a;
}
53
Mike Nakis

他のヘッダーファイルにヘッダーファイルを含めるのではなく、ソースファイルにヘッダーファイルを含めるだけです。

ヘッダーでは、前方宣言を使用できます。

// In Class1.h
class Class2;

// In class2.h
class Class1;

また、プリプロセッサを使用して、ファイルが2回インクルードされるのを防ぐことができます。

// Class1.h
#ifndef __CLASS_1_H
#define __CLASS_1_H

// content

#endif
21
Matt Lacey

私はこれが古いトピックであることを知っていますが、おそらくあなたはまだ解決策に興味があります!

実際、C++では、ポインターを使用せずに2つのクラスを再帰的に使用できます。その方法を次に示します。

ファイル:a.h

#include <b.h>

class A {
    B<> b;
}

ファイル:b.h

class A;

template<typename T = A>
class B {
    T a;
}

ファイル:main.cpp

#include "a.h"    
A a;

それだけです!

もちろん、これは単に好奇心のためです:)

14
Boynux

実際に各クラスのインスタンスを相互に配置する場合を除き、おそらく前方宣言を使用する必要があります。その場合、何も使用しないでください。

BがA内にのみ存在できる場合、ポインターを使用せずにAとBを作成できるようです。 BはAを単に前方宣言し、それを含めない(再帰的な包含を回避する)必要があります。

私の場合、Documentには、Sectionへの参照を取得するDocumentがあります。

section.h

class Document;

class Section
{
    public:
        Section(Document& document) : document{document} {} 
    private:
        Document& document;
};

document.h

#include "section.h"

class Document
{
    public:
        Document() : section{*this} {}
    private:
        Section section;
};

main.cpp

#include "document.h"

int main()
{
    Document document{};
}

このコードはg++でコンパイルされ、Linux上で実行されます。

ifdefの(複雑な)セットにより、他の場合に有効になる場合がありますが、読みやすさについてはわかりません...

0
a.l.e

前方宣言の可能性に加えて、相互に2つのクラスが相互に必要であると思われる場合、私の経験では継承の深さの間違いの兆候です。クラスは兄弟のようなものであり、両方の親クラスを作成する必要があります。または、実際には、この親クラスから兄弟を持つ必要があるクラス内の親クラスであるクラスを使用しようとしています。次に、この兄弟を3番目のクラスとして作成する必要があります。

0
Thomas_M