В моем проекте на C++, когда мне нужно использовать включение заголовочных файлов (#include "myclass.h"
)? И когда мне нужно использовать форвардную декларацию класса (class CMyClass;
)?Включение заголовков заголовков/декларация переадресации
ответ
Как правило, сначала попробуйте предварительную декларацию. Это сократит время компиляции и т. Д. Если это не скомпилируется, зайдите на #include
. Вы должны пойти на #include, если вам нужно выполнить одно из следующих действий:
- Доступ к члену или функции класса.
- Использовать указатель арифметики.
- Используйте sizeof.
- Любая информация RTTI.
new
/delete
, копия и т. Д.- Используйте его по значению.
- Наследуйте от него.
- Имейте это как член.
- Экземпляры в функции.
(6,7,8,9 от @Mooing Duck)
Они, вероятно, больше, но я не получил свой язык закона шляпу на сегодня.
также необходимо включить его используйте его по значению. Поэтому, если вы наследуете его, имеете его как член или имеете экземпляр в функции. –
Если вам нужен только указатель на класс, и вам не нужны какие-либо знания о классе, а не его имя, вы можете использовать декларацию forward.
В качестве новичка вы всегда должны # включать заголовочные файлы, когда вам нужно использовать типы или функции, которые они содержат, - не пытайтесь «оптимизировать» свою сборку, отправляя объявление вперед - это вряд ли когда-либо необходимо, даже на больших проектов, при условии, что проект хорошо архивирован.
Единственный раз, когда вы абсолютно необходимо опережающее объявление в подобных ситуациях:
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);
};
Выполнение технического обслуживания больших проектов с одним большим файлом «forward_declarations.h» может быть кошмаром, поскольку он часто путает инструмент разработки и затрудняет просмотр незнакомой базы кода. Хотя иногда бывает важно уменьшить сцепление в коде, не просто автоматически помещать каждый класс в файл переднего dec. – bd2357
Вы должны стремиться к минимизации ваших #include
S как для того, чтобы сократить время компиляции, но и помочь с модульностью и контролируемости. Как говорит @ypnos, классные переходы превосходны, когда вам нужны только указатели.
Для получения некоторых практических советов о том, как уменьшить зависимости заголовков, см., Например, this article.
Есть несколько проблем, которые пересылают заявление:
- Это походит на хранение своего имени класса в нескольких местах - если изменить ее в одном месте, теперь вы должны изменить его везде. Рефакторинг становится проблемой, поскольку код все равно будет компилироваться с измененным именем класса, но привязка не удастся, так как передовые объявления относятся к неопределенному классу. Если вы включите заголовочный файл и не используете форвардные объявления, вы поймаете эти проблемы во время компиляции.
Передовые декларации трудно поддерживать другим. Например, если файл заголовка содержит:
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 ;
} ;
Я согласен с проблемами @David для передовых деклараций. Но я не согласен с заключением: «Итак, когда это нормально, чтобы объявить объявление? Если вы делаете это в том же заголовочном файле, что и реальное объявление». Как указывали другие, форвардные декларации являются важным инструментом для развязки физического макета и ускорения сборки. // Все больше и больше мне интересно, следует ли нам включать # Foo.hf ".hf" в заголовочный файл, который ничего не содержит, кроме форвардных объявлений. " // Конечно, я бы автоматически сгенерировал это. –
См http://stackoverflow.com/questions/553682/when-to-use-forward-declaration – ChrisN