Де поставити включати заяви, заголовок або джерело?


106

Чи слід помістити включення у файл заголовка чи вихідний файл? Якщо файл заголовка містить оператори include, то якщо я включу цей файл заголовка у своє джерело, то чи буде у мого вихідного файлу всі включені файли, що були у моєму заголовку? Або я повинен просто включити їх у свій вихідний файл?


2
Багато попередніх дублікатів на SO, наприклад, де слід "включити", помістити в C ++
Paul R

Відповіді:


141

Поставити включає в заголовок лише те, якщо сам заголовок їм потрібен.

Приклади:

  • Ваша функція повертає тип 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. (з використанням покажчика немає цієї проблеми)
shuva

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

27

Протягом багатьох років з цього приводу було досить багато розбіжностей. Свого часу було традиційно, що заголовок оголошує лише те, що було в будь-якому модулі, з яким воно було пов’язане, тому багато заголовків мали конкретні вимоги, що ви #includeзадаєте певний набір заголовків (у певному порядку). Деякі надзвичайно традиційні програмісти C все ще дотримуються цієї моделі (релігійно, принаймні, у деяких випадках).

Останнім часом з'явився рух до того, щоб більшість заголовків були самостійними. Якщо для цього заголовка потрібно щось інше, сам заголовок обробляє це, гарантуючи, що все, що йому потрібно, буде включене (у правильному порядку, якщо є проблеми з замовленням). Особисто я віддаю перевагу цьому - особливо коли порядок заголовків може бути важливим, він вирішує проблему один раз, замість того, щоб вимагати від кожного, хто використовує її, ще раз вирішити проблему.

Зауважте, що більшість заголовків повинні містити лише декларації. Це означає, що додавання непотрібного заголовка не повинно (як правило) впливати на остаточний виконуваний файл. Найгірше, що трапляється, це те, що він трохи уповільнює компіляцію.


2
Якщо всі заголовки написані у другому стилі, проблем із замовленням взагалі не повинно бути. Замовлення проблем у заголовках зазвичай означає, що ви не включили до заголовка все необхідне.
Прощання SE

12

Ваш #includeфайл повинен містити файли заголовків, а кожен файл (джерело чи заголовок) - #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
Догунгонд гончаків

6

Підхід, до якого я розвинувся протягом двадцяти років, це такий;

Розгляньте бібліотеку.

Є кілька C-файлів, один внутрішній файл H і один зовнішній H-файл. Файли C містять внутрішній файл H. Внутрішній файл H включає зовнішній файл H.

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

зовнішній -> внутрішній -> код C

Це правильне впорядкування, оскільки те, що є зовнішнім, - це все, що потрібно третій стороні для використання бібліотеки. Те, що є внутрішнім, потрібно для складання коду С.


4

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

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

І кожен файл заголовка повинен містити захисну форму, щоб запобігти його включенню кілька разів.


4

У деяких середовищах компіляція буде найшвидшою, якщо в неї входять лише файли заголовків, які потрібні. В інших середовищах компіляція буде оптимізована, якщо всі вихідні файли можуть використовувати одну і ту ж первинну колекцію заголовків (деякі файли можуть мати додаткові заголовки поза загальним підмножиною). В ідеалі заголовки повинні бути побудовані таким чином, що кілька операцій #include не матимуть ефекту. Може бути добре оточити #include операторами перевірок на наявність файлу, який буде включений, включати-охороняти, хоча це створює залежність від формату цього захисту. Крім того, залежно від поведінки кешування файлів системи, зайве #include, ціль якого закінчується повністю # ifdef'ed, може не зайняти багато часу.

Інша річ, яку слід врахувати, це те, що якщо функція бере покажчик на структуру, прототип можна записати як

void foo (struct BAR_s * bar);

без визначення BAR_s, які повинні бути в області застосування. Дуже зручний підхід до уникнення зайвого включає.

PS - у багатьох моїх проектах буде файл, який, як очікується, кожен модуль буде #include, містить такі речі, як typedefs для цілих розмірів та кілька загальних структур та об'єднань [наприклад

typedef union {
  ненаписаний довгий l;
  ненаписаний короткий lw [2];
  непідписаний лідер [4];
} U_QUAD;

(Так, я знаю, що у мене виникнуть проблеми, якщо я перейшов до архітектури великого ендіану, але оскільки мій компілятор не дозволяє анонімним структурам в союзах, використання ідентифікованих ідентифікаторів для байтів у союзі вимагатиме доступу до них як theUnion.b.b1 тощо, що здається досить прикро.


3

Створіть усі свої файли так, щоб їх можна було скласти, використовуючи лише те, що вони містять. Якщо вам не потрібно включити у свій заголовок, видаліть його. У великому проекті, якщо ви не дотримуєтесь цієї дисципліни, ви залишаєте себе відкритим для розбиття цілої збірки, коли хтось видаляє включати з заголовкового файлу, який використовує споживач цього файлу, а навіть не заголовок.


1

Ваш вихідний файл буде містити заяви, якщо ви помістите його в заголовок. Однак у деяких випадках було б краще помістити їх у вихідний файл.

Пам'ятайте, що якщо ви включите цей заголовок у будь-які інші джерела, вони також отримають включення із заголовка, і це не завжди бажано. Ви повинні включати речі лише там, де він використовується.


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.