Форвардная декларация против Include

Рассмотрим следующие два сценария (Отредактировано только для того, чтобы закончить весь вопрос и сделать его более понятным)

Случай 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 выполнены вместе. Имеют ли два вышеуказанных сценария какие-либо различия? Есть ли причина предпочесть один метод над другим?

Изменить: Итак, как мне сделать сценарий 1?

Ответы

Ответ 1

Случай 1 приведет к ошибке "неполного типа" при компиляции B.cpp. Поскольку класс B содержит объект класса A, определение (и, в частности, размер) класса A требуется для завершения перед определением класса B.

В качестве альтернативы вы могли бы сделать some_variable указателем или ссылкой на класс A, и в этом случае ваше прямое объявление будет достаточным в B.h. Вам все равно нужно полное определение A в B.cpp(если вы действительно использовали функции/данные A-члена).

Ответ 2

Вперед объявление не является заменой включения заголовка заголовка.

Как следует из самого имени, прямое объявление - это просто Declaration, а не определение.

Итак, вы объявите компилятору, что это класс, и я просто объявляю его здесь и предоставит вам определение, когда я его буду использовать. Итак, обычно вы forward declare в файле заголовка и #include в .cpp файле, где вы используете членов объявленного вперед класса.

Таким образом, то, что вы делаете, везде, где вы включаете заголовочный файл, будет просто объявление для класса вместо всего содержимого #included...

Но, сказав, что, когда компилятор требует определения класса, он должен быть #included..

Итак, в вашем случае A obj; требуется определение class A и, следовательно, вы должны #include..

Я сам задал аналогичный вопрос здесь и другой похожий вопрос, который также имеет приятный ответ...

Надеюсь, что это поможет.

Ответ 3

Вам нужно использовать форвардные объявления в случаях, когда у вас есть классы, которые ссылаются друг на друга.

//A.h

class B;

class A {
    B* someVar;
}

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

class B {
    A* someVar;
}

Но нет никакой пользы для этого в случае, если вы выложили.

Ответ 4

Подумайте, как компилятор. Чтобы создать A внутри B, компилятор должен знать, как построить A, и единственный способ сделать это - получить полное определение. В переднем объявлении компилятор сообщает, что класс A существует без описания того, как он выглядит; это достаточно для определения указателя или ссылки. Когда придет время использовать этот указатель или ссылку, потребуется полное определение класса.

Ответ 5

Если вы хотите изобразить some_variable в качестве указателя, то часто рекомендуемая практика заключается в том, чтобы использовать форвардные объявления, когда это возможно, чтобы избежать накладных расходов включений и более длительных сроков компиляции.

Я использую лучшие практики, но мне очень нравится использовать IDE, которые имеют приятные функции навигации и пересылки кода, вызывают проблемы там, по крайней мере, с Netbeans. Всякий раз, когда я пытаюсь перейти к объявлению типа, я всегда оказываюсь в файле forward, а не в файле .h, содержащем фактическое объявление. Я готов принять дополнительное время компиляции для удобства навигации. Возможно, это просто проблема с Netbeans:)

.. о да.. Если вы посмотрите на связанные вопросы справа от вашего вопроса, вы найдете много дополнительной информации о форвардных декларациях.

Ответ 6

В случае 1 компилятор будет жаловаться на "неполный тип" для класса B, поскольку класс B содержит объект класса A, и вы не указали B какой-либо детали класса A, поэтому компилятор не может решить размер объекта B.

В вашем случае вы можете использовать A& obj или A* obj вместо A obj, так как размер ссылки/указателя const (4/8 для 32-битного/64-битного процессора).