次の2つのシナリオを検討してください(質問全体を完了して明確にするために編集)
ケース1 :(以下に正しく記載されているようにコンパイルされません)
//B.h
#ifndef B_H
#define B_H
#include "B.h"
class A;
class B {
A obj;
public:
void printA_thruB();
};
#endif
//B.cpp
#include "B.h"
#include <iostream>
void B::printA_thruB(){
obj.printA();
}
//A.h;
#ifndef A_H
#define A_H
#include "A.h"
class A {
int a;
public:
A();
void printA();
};
#endif
//A.cpp
#include "A.h"
#include <iostream>
A::A(){
a=10;
}
void A::printA()
{
std::cout<<"A:"<<a<<std::endl;
}
//main.cpp
#include "B.h"
#include<iostream>
using namespace std;
int main()
{
B obj;
obj.printA_thruB();
}
ケース2 :(唯一の変更...コンパイルエラーなしで機能します)
//B.h
#include "A.h" //Add this line
//class A; //comment out this line
A.cppとB.cppの両方が一緒にコンパイルされていると仮定しましょう。上記の2つのシナリオで違いはありますか?ある方法を他の方法よりも好む理由はありますか?
編集:では、シナリオ1を機能させるにはどうすればよいですか。
ケース1では、B.cppをコンパイルすると、「不完全な型」エラーが発生します。クラスBにはクラスAオブジェクトが含まれているため、クラスAの定義(特にサイズ)は、クラスBの定義の前に完了する必要があります。
または、some_variableをクラスAへのポインターまたは参照にすることを選択できます。その場合、B.hでは前方宣言で十分です。 B.cppでAの完全な定義が必要です(Aメンバーの関数/データを実際に使用したと仮定します)。
前方宣言はヘッダーファイルインクルードの代わりにはなりません。
名前自体が示すように、前方宣言は単なるDeclaration
であり、定義ではありません。
したがって、コンパイラにクラスであると宣言し、ここで宣言するだけで、使用するときに定義を提供します。したがって、通常は、ヘッダーファイルでforward declare
を、.cppファイルで#include
を使用します。ここで、のメンバーを使用します前方宣言されたクラス。
そうすることで、ヘッダーファイルをインクルードする場所はどこでも、コンテンツ全体ではなく、クラスの宣言だけが作成されます#included
...
ただし、コンパイラがクラスの定義を必要とする場合は、#included
である必要があります。
したがって、あなたの場合、A obj;
にはclass A
の定義が必要であり、したがって#include
。
私自身も同様の質問をしました ここ そして別の 同様の質問 これもいい答えがあります...
それが役に立てば幸い..
相互に参照するクラスがある場合は、前方宣言を使用する必要があります。
//A.h
class B;
class A {
B* someVar;
}
//B.h
#include <A.h>
class B {
A* someVar;
}
しかし、あなたがレイアウトした場合、それを行うことにメリットはありません。
コンパイラのように考えてください。 A
内にB
を作成するには、コンパイラーはA
の作成方法を知っている必要があり、そのための唯一の方法は完全な定義を持つことです。前方宣言は、クラスA
がどのように見えるかを記述せずに、存在することをコンパイラーに通知します。これは、ポインタまたは参照を定義するのに十分です。そのポインタまたは参照を使用するときは、完全なクラス定義が必要になります。
Some_variableをポインターとして表現する場合、インクルードのオーバーヘッドとコンパイル時間の延長を回避するために、可能な限り前方宣言を使用することをお勧めします。
私はすべてベストプラクティスに賛成ですが、Niceコードナビゲーション機能を備えたIDEを使用するのが本当に好きで、少なくともNetbeansでは転送によって問題が発生します。型宣言に移動しようとすると、実際の宣言を含む.hファイルではなく、常に前方に移動します。ナビゲーションを簡単にするために、追加のコンパイル時間を受け入れたいと思います。たぶん、これはNetbeansの問題です:)
..そうそう..質問の右側にある関連する質問を見ると、前方宣言の追加情報がたくさん見つかります。
ケース1の場合、クラスBにはクラスAオブジェクトが含まれており、クラスAの詳細をBに通知しなかったため、コンパイラはクラスBの「不完全な型」で文句を言います。そのため、コンパイラはオブジェクトBのサイズを決定できません。
参照/ポインタのサイズがconst(32ビット/ 64ビットCPUの場合は4/8)であるため、A& obj
の代わりにA* obj
またはA obj
を使用できます。