web-dev-qa-db-ja.com

前方宣言と含める

次の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を機能させるにはどうすればよいですか。

21
Sii

ケース1では、B.cppをコンパイルすると、「不完全な型」エラーが発生します。クラスBにはクラスAオブジェクトが含まれているため、クラスAの定義(特にサイズ)は、クラスBの定義の前に完了する必要があります。

または、some_variableをクラスAへのポインターまたは参照にすることを選択できます。その場合、B.hでは前方宣言で十分です。 B.cppでAの完全な定義が必要です(Aメンバーの関数/データを実際に使用したと仮定します)。

13
Drew Hall

前方宣言はヘッダーファイルインクルードの代わりにはなりません。

名前自体が示すように、前方宣言は単なるDeclarationであり、定義ではありません。

したがって、コンパイラにクラスであると宣言し、ここで宣言するだけで、使用するときに定義を提供します。したがって、通常は、ヘッダーファイルでforward declareを、.cppファイルで#includeを使用します。ここで、のメンバーを使用します前方宣言されたクラス。

そうすることで、ヘッダーファイルをインクルードする場所はどこでも、コンテンツ全体ではなく、クラスの宣言だけが作成されます#included ...

ただし、コンパイラがクラスの定義を必要とする場合は、#includedである必要があります。

したがって、あなたの場合、A obj;にはclass Aの定義が必要であり、したがって#include

私自身も同様の質問をしました ここ そして別の 同様の質問 これもいい答えがあります...

それが役に立てば幸い..

11
liaK

相互に参照するクラスがある場合は、前方宣言を使用する必要があります。

//A.h

class B;

class A {
    B* someVar;
}

//B.h
#include <A.h>

class B {
    A* someVar;
}

しかし、あなたがレイアウトした場合、それを行うことにメリットはありません。

4
Hitesh

コンパイラのように考えてください。 A内にBを作成するには、コンパイラーはAの作成方法を知っている必要があり、そのための唯一の方法は完全な定義を持つことです。前方宣言は、クラスAがどのように見えるかを記述せずに、存在することをコンパイラーに通知します。これは、ポインタまたは参照を定義するのに十分です。そのポインタまたは参照を使用するときは、完全なクラス定義が必要になります。

3
Mark Ransom

Some_variableをポインターとして表現する場合、インクルードのオーバーヘッドとコンパイル時間の延長を回避するために、可能な限り前方宣言を使用することをお勧めします。

私はすべてベストプラクティスに賛成ですが、Niceコードナビゲーション機能を備えたIDEを使用するのが本当に好きで、少なくともNetbeansでは転送によって問題が発生します。型宣言に移動しようとすると、実際の宣言を含む.hファイルではなく、常に前方に移動します。ナビゲーションを簡単にするために、追加のコンパイル時間を受け入れたいと思います。たぶん、これはNetbeansの問題です:)

..そうそう..質問の右側にある関連する質問を見ると、前方宣言の追加情報がたくさん見つかります。

2
Arnold Spence

ケース1の場合、クラスBにはクラスAオブジェクトが含まれており、クラスAの詳細をBに通知しなかったため、コンパイラはクラスBの「不完全な型」で文句を言います。そのため、コンパイラはオブジェクトBのサイズを決定できません。

参照/ポインタのサイズがconst(32ビット/ 64ビットCPUの場合は4/8)であるため、A& objの代わりにA* objまたはA objを使用できます。

0
mike