it-swarm.com.ru

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

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

96
Mohit Deshpande

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

Примеры:

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

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

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

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

24
Jerry Coffin

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

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

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

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

Подход, в который я развился более чем за двадцать лет, таков;

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

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

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

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

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

4
user82238

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

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

 void foo (struct BAR_s * bar); 

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

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

 typedef union {
 unsigned long l; 
 unsigned short lw [2]; 
 unsigned char lb [4]; 
} U_QUAD; 

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

4
supercat

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

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

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

4
Vicky

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

3
rerun

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

#ifndef NAME_OF_HEADER_H
#define NAME_OF_HEADER_H

...definition of header file...

#endif

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

1
JRam930

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

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

1
Splashdust