2010-04-28 5 views
3

Может ли кто-нибудь объяснить, почему в заголовочных файлах есть что-то вроде этого?класс foo; в заголовочном файле

class foo; // This here? 
class bar 
{ 
    bar(); 

}; 

Нужно ли включать заявление при использовании этого?

Спасибо.

ответ

10

Первый class foo; называется forward declaration класса foo. Он просто позволяет компилятору знать, что он существует, и что он называет класс. Это делает foo тем, что называется «неполным типом» (если полное объявление foo уже не было замечено). С неполным типом вы можете объявлять указатели этого типа, но вы не можете выделять экземпляры этого типа или делать что-либо, требующее знания его размера или членов.

Такие форвардные декларации часто используются, когда два типа могут иметь указатели друг на друга, и в этом случае оба должны быть способны выразить понятие указателя на другой тип, и поэтому у вас будет круговая зависимость без такая вещь. Это необходимо в основном потому, что C++ использует один проходный механизм для разрешения типов; в Java вы можете иметь циклические зависимости без форвардных объявлений, потому что Java использует несколько проходов. Вы также можете видеть передовые декларации, когда автор находится под ошибочным впечатлением, что использование форвардных объявлений вместо включения требуемого заголовка сокращает время компиляции; это, конечно, не так, потому что вам нужно включить полную декларацию (т. е. заголовок), так или иначе, и если используются препроцессорные охранники, то в отличие от времени компиляции нет никакой разницы.

Чтобы ответить на ваш вопрос о том, нужно ли вам включать или нет, предполагается, что вам нужен только частичный тип, тогда ваш заголовок не должен напрямую включать заголовок для типа, который был объявлен вперед; однако тот, кто использует ваш заголовок, когда они используют ваш тип, должен будет включить заголовок для объявленного вперед типа, и поэтому вы можете просто включить другой заголовок.

+0

+1. Некоторая незначительная нить: то, что вы называете «полная декларация», на самом деле является «определением». – sellibitze

2

Это forward declaration. Вам это нужно, например, если класс bar имеет указатель на объект foo, но вы не хотите сразу включать полное определение объекта foo.

0

Это передовая декларация. Рассмотрим следующий пример:

class foo; // you likely need this for the code beneath to compile 
class bar { 
    void smth(foo&); 
}; 

Если вы не включили определение class foo таким образом, что компилятор видит перед компиляцией определение class bar код не будет компилировать (компилятор скажет, что Безразлично» t знать, что означает foo), если у вас нет прямого объявления.

+0

Определение не является необходимым. Декларации достаточно. В вашем примере показано объявление foo. – sellibitze

1

Это передовая декларация класса. По моему опыту, это обычно делается, когда у вас есть циклическая зависимость .. например

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; 
} 
0

Это форварды объявление класса «Foo». Это позволяет вам указывать указатели и ссылки на класс, но не использовать (например, члены вызова или определить его размер), потому что он еще не определен! Впоследствии оно должно быть дополнено полным нормальным заявлением (class foo { ... };).

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

+0

То, что вы называете «нормальная декларация», - это определение – sellibitze

0

Это называется форвардной декларацией. Тело класса 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; 
}; 
0

Это называется форвардной декларацией. Он используется, чтобы ваш код знал, что класс foo существует. Это, в свою очередь, может использоваться панелью классов.

Он обычно используется для решения круговых задач. Примите это, например

//a.h 
#include "b.h" 

class A 
{ 
    void useB(B obj); 
} 

и

// b.h 
#include "a.h" 
class B 
{ 
    void useA(A obj); 
} 

Это приводит к круговой включать проблему, так как включает в себя хиджры b.h который в свою очередь включает в себя хиджры, до бесконечности. Вы можете решить эту проблему, сделав опережающее объявление каждого класса в каждом заголовке, например, так:

//a.h 
class B; 

class A 
{ 
    void useB(B obj); 
} 

// b.h 
class A; 

class B 
{ 
    void useA(A obj); 
} 

Примечание: Очень часто, когда у вас есть циркуляр включают проблемы, это может свидетельствовать о проблемах зачатия/моделирования , Вероятно, вы должны спросить себя, четко ли определены ваши классы, прежде чем пытаться решить вашу проблему с помощью передовых объявлений.

1

Любопытно, зачем нам нужен термин форвардная декларация? Не является ли декларация просто декларацией (в отличие от определения)?

class X; // declaration 

class X // definition 
{ 
    int member; 
    void function(); 
}; 
+0

+1. Да. :-) – sellibitze

0

Try думать о написании этого:

файл bar.h:

 #include "bar.h" 

     class Foo 
     { 
     Bar* bar_ptr; 
     } 

файл foo.h:

 #include "foo.h" 

     class Bar 
     { 
      Foo* foo_ptr; 
     } 

Это не будет работать, первый из-за к бесконечной цепочке #include, то, если вы избавитесь от одного из включений, либо Foo не будет знать, что такое Bar, либо Bar не будет знать, что такое Foo.

Попробуйте вместо этого:

 class Bar; 

     class Foo 
     { 
     Bar* bar_ptr; 
     }; 

файл foo.h:

 class Foo; 

     class Bar 
     { 
      Foo* foo_ptr; 
     }; 
Смежные вопросы