Куда поместить инструкции включения, заголовок или источник?


107

Должен ли я помещать включения в файл заголовка или исходный файл? Если файл заголовка содержит операторы include, то если я включу этот файл заголовка в свой источник, тогда будет ли в моем исходном файле все включенные файлы, которые были в моем заголовке? Или я должен просто включить их только в исходный файл?


2
Многие предыдущие дубликаты на SO, например, куда следует поместить «include» в C ++
Paul R

Ответы:


142

Включайте в заголовок только в том случае, если они нужны самому заголовку.

Примеры:

  • Ваша функция возвращает тип size_t. Затем #include <stddef.h>в заголовочном файле.
  • Ваша функция использует strlen. Затем #include <string.h>в исходном файле.

2
Что, если моя функция принимает аргумент типа size_t?
andrybak

Тот же вопрос расширяется до c ++: что, если моя структура / класс имеет поле / член типа size_tили std::string?
andrybak

10
В чем причина?
Патрицио Бертони

У меня подключенная ситуация, класс C ++ имеет объект другого класса B, и я не могу использовать предварительное объявление B и конечную точку, включая заголовок B внутри заголовка A. (использование указателя не вызывает этой проблемы)
шува

@andrybak Ваши исходные файлы должны включать ваш файл заголовка, чтобы все, что включает ваш заголовок, получило и ваш источник.
Джереми Трифило

27

На протяжении многих лет по этому поводу было довольно много разногласий. Когда-то было традиционным, что заголовок объявлял только то, что было в каком-либо модуле, к которому он был связан, поэтому многие заголовки имели определенные требования, что вам #includeнужен определенный набор заголовков (в определенном порядке). Некоторые чрезвычайно традиционные программисты на C все еще следуют этой модели (по крайней мере, в некоторых случаях религиозно).

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

Обратите внимание, что большинство заголовков должны содержать только объявления. Это означает, что добавление ненужного заголовка (обычно) не должно иметь никакого влияния на ваш окончательный исполняемый файл. Худшее, что случается, - это немного замедляет компиляцию.


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

12

Ваш #includes должен быть из файлов заголовков, и каждый файл (исходный или заголовочный) должен содержать файлы #includeзаголовков, которые ему нужны. Заголовочные файлы должны иметь #includeнеобходимый минимум заголовочных файлов, и исходные файлы также должны, хотя это не так важно для исходных файлов.

Исходный файл будет иметь заголовки #include, а также их заголовки #includeи так далее до максимальной глубины вложения. Вот почему вам не нужны лишние #includes в файлах заголовков: они могут привести к тому, что исходный файл будет включать множество файлов заголовков, которые могут не понадобиться, замедляя компиляцию.

Это означает, что вполне возможно, что файлы заголовков могут быть включены дважды, и это может быть проблемой. Традиционный метод состоит в том, чтобы добавить "охранников включения" в файлы заголовков, например, для файла foo.h:

#ifndef INCLUDE_FOO_H
#define INCLUDE_FOO_H
/* everything in header goes here */
#endif

Я знаю, что этот ответ очень старый, но с тех пор они добавили #pragma один раз, поэтому вы не должны включать #ifndef при объявлении #includes, я опубликовал это, потому что старые, но более популярные / получившие голоса темы, как правило, находятся в верхней части поисковых запросов Google
Dogunbound hounds

6

Подход, который я выработал более двадцати лет, таков;

Рассмотрим библиотеку.

Есть несколько файлов C, один внутренний H-файл и один внешний H-файл. Файлы C включают внутренний файл H. Внутренний файл H включает внешний файл H.

Вы видите, что из POV компилятора, когда он компилирует файл C, существует иерархия;

внешний -> внутренний -> код C

Это правильный порядок, поскольку внешнее - это все, что нужно третьей стороне для использования библиотеки. То, что является внутренним, требуется для компиляции кода C.


4

Если файл заголовка #includesфайлы заголовков В и С, то каждый исходный файл , #includesА будет также получить B и C #included. Препроцессор буквально выполняет замену текста: везде, где он находит текст, говорится, что #include <foo.h>он заменяет его текстом foo.hфайла.

Существуют разные мнения о том, следует ли вставлять #includesзаголовочные файлы или исходные файлы. Лично я предпочитаю помещать все #includesв исходный файл по умолчанию, но любые файлы заголовков, которые не могут быть скомпилированы без других предварительных заголовков, должны использовать #includeсами заголовки.

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


4

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


4

В некоторых средах компиляция будет самой быстрой, если в нее будут включены только нужные файлы заголовков. В других средах компиляция будет оптимизирована, если все исходные файлы могут использовать одну и ту же основную коллекцию заголовков (некоторые файлы могут иметь дополнительные заголовки помимо общего подмножества). В идеале заголовки должны быть созданы так, чтобы несколько операций #include не имели никакого эффекта. Было бы неплохо окружить операторы #include проверками include-guard для включаемого файла, хотя это создает зависимость от формата этого средства защиты. Кроме того, в зависимости от поведения кэширования файлов в системе, необязательный #include, цель которого оказывается полностью удаленным # ifdef, может не занять много времени.

Также следует учитывать, что если функция принимает указатель на структуру, можно записать прототип как

void foo (struct BAR_s * bar);

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

PS - во многих моих проектах будет файл, который, как ожидается, каждый модуль будет #include, содержащий такие вещи, как typedef для целочисленных размеров и несколько общих структур и объединений [например,

typedef union {
  беззнаковое длинное l;
  беззнаковый короткий lw [2];
  беззнаковый символ lb [4];
} U_QUAD;

(Да, я знаю, что у меня будут проблемы, если я перейду на архитектуру с прямым порядком байтов, но поскольку мой компилятор не разрешает анонимные структуры в объединениях, использование именованных идентификаторов для байтов в объединении потребует доступа к ним как theUnion.b.b1 и т.д., что кажется довольно раздражающим.


1

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

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


1

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

#ifndef NAME_OF_HEADER_H
#define NAME_OF_HEADER_H

...definition of header file...

#endif

Это предотвращает включение заголовка несколько раз, что приведет к ошибке компилятора.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.