Возможный дубликат:
где «включить» следует поместить в C ++
Очевидно, что есть две «школы мысли» о том, стоит ли #include
директивы в заголовочные файлы C ++ (или, в качестве альтернативы, положить #include
только в файлы cpp). Некоторые люди говорят, что все в порядке, другие говорят, что это только вызывает проблемы. Кто-нибудь знает, пришел ли этот разговор к выводу, что следует отдавать предпочтение?
Я не знаю ни одной школы мысли об этом. Поместите их в заголовок, когда они там понадобятся, в противном случае объявите и отправьте их в .cpp
файлы, которые требуют их. Нет смысла включать заголовки там, где они не нужны.
То, что я нашел эффективным, следует нескольким простым правилам
Получить первый пункт довольно просто: сначала включите заголовок из исходного кода, реализуя то, что он объявляет. Точно получить вторую точку — это не тривиально, и я думаю, что для ее получения требуется поддержка инструмента. Тем не менее, несколько ненужных зависимостей, как правило, не так уж и плохо.
Как правило, вы не включаете заголовки в заголовок, если там требуется их полное определение. Большую часть времени вы играете с указателями классов в заголовочном файле, так что будет просто объявить их там.
Я думаю, что проблема была решена давным-давно: заголовки должны быть автономными (то есть не должно зависеть от того, что пользователь включил другие заголовки раньше — этот аспект исчерпан так долго, что некоторые даже не осознают, что спор об этом, но ваш положить включает только в .cpp кажется, намекает на это), но минимальный (то есть не должен включать определения, когда декларации будет достаточно для самодостаточности).
Причиной самодостаточности является обслуживание: если заголовок будет изменен и теперь будет зависеть от чего-то нового, вам придется отслеживать все места, в которых он используется для включения новой зависимости. Кстати, стандартная уловка для обеспечения самодостаточности состоит в том, чтобы включить заголовок, обеспечивающий объявления для вещей, определенных в .cpp, сначала в .cpp.
Это не столько школы мысли, сколько религии. На самом деле, оба подхода имеют свои преимущества и недостатки, и существуют определенные практики, которым необходимо следовать, чтобы любой подход был успешным. Но только один из этих подходов «масштабируется» до крупных проектов.
Преимущество отсутствия заголовков внутри заголовков заключается в более быстрой компиляции. Однако это преимущество не связано с тем, что заголовки читаются только один раз, потому что даже если вы включите заголовки в заголовки, умные компиляторы могут это решить. Преимущество в скорости связано с тем, что вы включаете только те заголовки, которые строго необходимы для данного исходного файла. Другое преимущество состоит в том, что если мы посмотрим на исходный файл, то сможем точно увидеть его зависимости: плоский список заголовочных файлов дает нам это ясно.
Однако эту практику трудно поддерживать, особенно в больших проектах со многими программистами. Это довольно неудобно, когда вы хотите использовать модуль foo
, но вы не можете просто #include "foo.h"
: вам нужно включить 35 других заголовков.
В итоге происходит следующее: программисты не собираются тратить свое время на поиск точного, минимального набора заголовков, который им нужен просто для добавления модуля. foo
, Чтобы сэкономить время, они пойдут к какому-нибудь исходному файлу примера, подобному тому, над которым они работают, и вырежут и вставят все #include
директивы. Затем они попытаются скомпилировать его, и если он не скомпилируется, то они будут вырезать и вставлять больше #include
директивы еще из другого места, и повторять это, пока он не работает.
В результате вы постепенно теряете преимущество более быстрой компиляции, потому что ваши файлы теперь содержат ненужные заголовки. Кроме того, список #include
директивы больше не показывают истинные зависимости. Более того, когда вы делаете инкрементные компиляции сейчас, вы компилируете больше, чем необходимо из-за этих ложных зависимостей.
Как только каждый исходный файл включает в себя почти каждый заголовок, вы можете также иметь большой everything.h
который включает в себя все заголовки, а затем #include "everything.h"
в каждом исходном файле.
Так что эту практику включения только определенных заголовков лучше всего оставить небольшим проектам, которые тщательно поддерживаются горсткой разработчиков, у которых есть достаточно времени, чтобы поддерживать этику минимального включения зависимостей вручную или писать инструменты для поиска ненужных #include
директивы.