ヘッダーファイルにこのようなものがある理由を説明できる人はいますか?
class foo; // This here?
class bar
{
bar();
};
これを使用するときにincludeステートメントが必要ですか?
ありがとう。
ヘッダーファイルにこのようなものがある理由を説明できる人はいますか?
class foo; // This here?
class bar
{
bar();
};
これを使用するときにincludeステートメントが必要ですか?
ありがとう。
1つ目は、クラスfooの前方宣言class foo;
と呼ばれます。コンパイラに、それが存在し、クラスに名前を付けていることを通知するだけです。これにより、fooは「不完全な型」と呼ばれるものになります(fooの完全な宣言がすでに確認されている場合を除く)。不完全な型では、その型のポインターを宣言できますが、その型のインスタンスを割り当てたり、そのサイズやメンバーを知る必要があることを行うことはできません。
このような前方宣言は、2つの型がそれぞれ相互にポインターを持つ可能性がある場合に頻繁に使用されます。その場合、両方が他の型へのポインターの概念を表現できる必要があるため、そのようなことなしに循環依存関係があります。これは主に、C++が型を解決するためにシングルパスメカニズムを使用するために必要です。Javaでは、Javaは複数のパスを使用するため、前方宣言なしで循環依存関係を持つことができます。また、必要なヘッダーを含める代わりに前方宣言を使用するとコンパイル時間が短縮されるという誤った印象を著者が受けている前方宣言が表示される場合があります。もちろん、そうではありません。とにかく、完全な宣言(つまり、ヘッダー)を含める必要があり、プリプロセッサガードが使用されている場合、コンパイル時間に基本的に違いはありません。
インクルードが必要かどうかに関する質問に答えるには...部分的な型のみが必要であると仮定すると、ヘッダーには、前方宣言された型のヘッダーを直接含める必要はありません。ただし、ヘッダーを使用する人は誰でも、型を使用するときに前方宣言型のヘッダーを含める必要があるため、他のヘッダーを含めることもできます。
これは前方宣言です。たとえば、クラスバーにfooオブジェクトへのポインタがあるが、fooオブジェクトの定義全体をすぐに含めたくない場合に必要です。
これはクラスの前方宣言です。私の経験では、これは通常、循環依存がある場合に行われます。たとえば、
in foo.h
--------
#include "bar.h"
class foo
{
public:
foo(bar *bar);
private:
foo *m_foo;
};
and in bar.h
------------
class foo;
class bar
{
public:
void methodFooWillCall();
protected:
std::list<foo *> myFoos;
}
不思議なことに、なぜ前方宣言という用語が必要なのですか?前方宣言は(定義ではなく)単なる宣言ではありませんか?
class X; // declaration
class X // definition
{
int member;
void function();
};
これは前方宣言です。次の例を考えてみましょう。
class foo; // you likely need this for the code beneath to compile
class bar {
void smth( foo& );
};
class foo
コンパイルする前にコンパイラーがそれを見るような方法での定義を含めなかった場合、前方宣言がない限りclass bar
、コードの定義はコンパイルされません(コンパイラーはそれが何を意味するのかわからないと言うでしょう)。foo
これは、クラス'foo'の転送宣言です。クラスへのポインタと参照を宣言できますが、まだ定義されていないため、使用しないでください(たとえば、メンバーを呼び出すか、そのサイズを決定します)。後で、完全な通常の宣言(class foo { ... };
)でフォローアップする必要があります。
これは、相互にポインタを保持する2つのクラスを宣言する場合などに役立ちます。そうしないと、設定が不可能になります。
これは前方宣言と呼ばれます。クラスの本体はfoo
、ファイルの後の部分で定義されます。循環依存を回避するために前方宣言が行われます。クラスBarの定義にはクラスFooが必要であり、その逆も同様です。
class Bar
{
Foo * foo;
};
class Foo
{
Bar * bar;
};
ご覧のとおりBar
、への参照がFoo
あり、その逆もあります。これをコンパイルしようとすると、コンパイラはについて何も知らないと文句を言いFoo
ます。class Foo
解決策は、上記を前方宣言することBar
です(関数のプロトタイプを上記で宣言し、main
後でその本体を定義するのと同じように)。
class Foo; //Tells the compiler that there is a class Foo coming down the line.
class Bar
{
Foo * foo;
};
class Foo
{
Bar * bar;
};
これは前方宣言と呼ばれます。これは、クラスfooが存在することをコードに認識させるために使用されます。これは、クラスバーで使用できます。
これは、循環インクルードの問題を解決するために一般的に使用されます。これを例に取ってください
//a.h
#include "b.h"
class A
{
void useB(B obj);
}
と
// b.h
#include "a.h"
class B
{
void useA(A obj);
}
ahにはbhが含まれ、bhにはahが含まれるため、これにより循環インクルードの問題が発生します。この問題は、次のように、各ヘッダーで各クラスの前方宣言を行うことで解決できます。
//a.h
class B;
class A
{
void useB(B obj);
}
// b.h
class A;
class B
{
void useA(A obj);
}
注:循環インクルードの問題がある場合、これは概念/モデリングの問題を示していることがよくあります。前方宣言の問題を解決する前に、クラスが適切に定義されているかどうかを自問する必要があります。
これを書くことを考えてみてください:
ファイルbar.h:
#include "bar.h"
class Foo
{
Bar* bar_ptr;
}
ファイルfoo.h:
#include "foo.h"
class Bar
{
Foo* foo_ptr;
}
これは、最初は無限の#includeチェーンが原因で機能しません。次に、インクルードの1つを削除すると、FooはBarが何であるかを知らないか、BarはFooが何であるかを知りません。
代わりにこれを試してください:
class Bar;
class Foo
{
Bar* bar_ptr;
};
ファイルfoo.h:
class Foo;
class Bar
{
Foo* foo_ptr;
};