2010-05-14 3 views
10

В моем проекте на C++, когда мне нужно использовать включение заголовочных файлов (#include "myclass.h")? И когда мне нужно использовать форвардную декларацию класса (class CMyClass;)?Включение заголовков заголовков/декларация переадресации

+1

См http://stackoverflow.com/questions/553682/when-to-use-forward-declaration – ChrisN

ответ

16

Как правило, сначала попробуйте предварительную декларацию. Это сократит время компиляции и т. Д. Если это не скомпилируется, зайдите на #include. Вы должны пойти на #include, если вам нужно выполнить одно из следующих действий:

  1. Доступ к члену или функции класса.
  2. Использовать указатель арифметики.
  3. Используйте sizeof.
  4. Любая информация RTTI.
  5. new/delete, копия и т. Д.
  6. Используйте его по значению.
  7. Наследуйте от него.
  8. Имейте это как член.
  9. Экземпляры в функции.

(6,7,8,9 от @Mooing Duck)

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

+4

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

10

Если вам нужен только указатель на класс, и вам не нужны какие-либо знания о классе, а не его имя, вы можете использовать декларацию forward.

2

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

Единственный раз, когда вы абсолютно необходимо опережающее объявление в подобных ситуациях:

struct A { 
    void f(B b); 
}; 

struct B { 
    void f(A a); 
}; 

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

struct B; // forward declaration 

struct A { 
    void f(B b); 
}; 

struct B { 
    void f(A a); 
}; 
+0

Выполнение технического обслуживания больших проектов с одним большим файлом «forward_declarations.h» может быть кошмаром, поскольку он часто путает инструмент разработки и затрудняет просмотр незнакомой базы кода. Хотя иногда бывает важно уменьшить сцепление в коде, не просто автоматически помещать каждый класс в файл переднего dec. – bd2357

2

Вы должны стремиться к минимизации ваших #include S как для того, чтобы сократить время компиляции, но и помочь с модульностью и контролируемости. Как говорит @ypnos, классные переходы превосходны, когда вам нужны только указатели.

Для получения некоторых практических советов о том, как уменьшить зависимости заголовков, см., Например, this article.

11

Есть несколько проблем, которые пересылают заявление:

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

    include "MyProject/MyWidgets/MyFooWidgets/FooUtil/Foo.h" 
    

    , а не опережающее объявление

    class Foo ; 
    

    легко для других, чтобы найти, где класс Foo объявлен. С декларированием вперед, это не так, что очевидно; некоторые IDE, такие как Eclipse, могут открывать прямое объявление, когда пользователь пытается открыть объявление переменной .

  • Связывание может завершиться неудачей с неопределенными ошибками символов, когда вы включаете заголовочный файл в свой код, который содержит декларацию вперед, но фактическое определение кода находится в другой библиотеке, с которой вы не ссылались. Удобнее поймать эту проблему во время компиляции с ошибками, такими как "Could not find file MyProject/MyWidgets/MyFooWidgets/FooUtil/Foo.h", с тех пор вы будете знать, где искать соответствующие Foo.cpp и идентифицировать библиотеку, которая ее содержит.

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

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

Пример:

class Foo ; 

typedef Foo* FooPtr ; 
typedef Foo& FooRef ; 

class Foo 
{ 
    public: 
     Foo() ; 
     ~Foo() ; 
} 

ИЛИ

class TreeNode ; 

class Tree 
{ 
private: 
    TreeNode m_root ; 
} 

class TreeNode 
{ 
    void* m_data ; 
} ; 
+1

Я согласен с проблемами @David для передовых деклараций. Но я не согласен с заключением: «Итак, когда это нормально, чтобы объявить объявление? Если вы делаете это в том же заголовочном файле, что и реальное объявление». Как указывали другие, форвардные декларации являются важным инструментом для развязки физического макета и ускорения сборки. // Все больше и больше мне интересно, следует ли нам включать # Foo.hf ".hf" в заголовочный файл, который ничего не содержит, кроме форвардных объявлений. " // Конечно, я бы автоматически сгенерировал это. –

Смежные вопросы