C / C ++ включает порядок заголовочных файлов - PullRequest
248 голосов
/ 04 мая 2010

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

Например, системные файлы, STL и Boost отправляются до или после локальных включаемых файлов?

Ответы [ 10 ]

254 голосов
/ 04 мая 2010

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

Мое личное предпочтение - переходить с локального на глобальный, каждый подраздел в алфавитном порядке, т.е.

  1. h файл, соответствующий этому файлу cpp (если применимо)
  2. заголовки из того же компонента,
  3. заголовки от других компонентов,
  4. системные заголовки.

Мое обоснование для 1. состоит в том, что он должен доказать, что каждый заголовок (для которого есть cpp) может быть #include d без предварительных условий. А все остальное кажется логически вытекающим оттуда.

87 голосов
/ 09 января 2013

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

В частности, это упоминается в статье «Мышление в C ++», ссылаясь на «Крупномасштабное проектирование программного обеспечения C ++» Лакоса:

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

То есть включить в следующем порядке:

  1. Заголовок прототипа / интерфейса для этой реализации (т. Е. Файл .h / .hh, соответствующий этому файлу .cpp / .cc).
  2. Другие заголовки из того же проекта, по мере необходимости.
  3. Заголовки из других нестандартных несистемных библиотек (например, Qt, Eigen и т. Д.).
  4. Заголовки из других «почти стандартных» библиотек (например, Boost)
  5. Стандартные заголовки C ++ (например, iostream, функционал и т. Д.)
  6. Стандартные заголовки C (например, cstdint, dirent.h и т. Д.)

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

Руководство по стилю Google C ++ утверждает почти наоборот, без какого-либо обоснования; Я лично склоняюсь в пользу подхода Лакоса.

48 голосов
/ 04 мая 2010

Я следую двум простым правилам, которые избегают подавляющего большинства проблем:

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

Я также следую указаниям:

  1. Сначала включите системные заголовки (stdio.h и т. Д.) С разделительной линией.
  2. Группируйте их логически.

Другими словами:

#include <stdio.h>
#include <string.h>

#include "btree.h"
#include "collect_hash.h"
#include "collect_arraylist.h"
#include "globals.h"

Хотя, будучи руководством, это субъективная вещь. С другой стороны, я строго соблюдаю правила, вплоть до предоставления заголовочных файлов «обертки» с включенными защитами и сгруппированными включениями, если какой-то противный сторонний разработчик не подпишется на мое видение: -)

20 голосов
/ 04 мая 2010

Чтобы добавить мой собственный кирпич к стене.

  1. Каждый заголовок должен быть самодостаточным, что может быть проверено, только если он включен первым хотя бы один раз
  2. Нужноне по ошибке измените значение стороннего заголовка, введя символы (макро, типы и т. д.)

Поэтому я обычно так:

// myproject/src/example.cpp
#include "myproject/example.h"

#include <algorithm>
#include <set>
#include <vector>

#include <3rdparty/foo.h>
#include <3rdparty/bar.h>

#include "myproject/another.h"
#include "myproject/specific/bla.h"

#include "detail/impl.h"

Каждая группа отделеначерез пустую строку следующего:

  • Заголовок, соответствующий первому этому файлу cpp (проверка работоспособности)
  • Системные заголовки
  • Сторонние заголовки, организованныепорядок зависимостей
  • Заголовки проекта
  • Частные заголовки проекта

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

14 голосов
/ 04 мая 2010

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

#include <set>
#include <vector>
#include <algorithm>
#include <functional>

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

13 голосов
/ 04 мая 2010

Рекомендую:

  1. Заголовок для создаваемого вами модуля .cc. (Помогает убедиться, что каждый заголовок в вашем проекте не имеет неявных зависимостей от других заголовков в вашем проекте.)
  2. C системных файлов.
  3. Системные файлы C ++.
  4. Платформа / OS / другие файлы заголовков (например, win32, gtk, openGL).
  5. Другие заголовочные файлы из вашего проекта.

И, конечно, в алфавитном порядке в каждом разделе, где это возможно.

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

6 голосов
/ 04 мая 2010

Это не субъективно. Убедитесь, что ваши заголовки не полагаются на #include d в определенном порядке. Вы можете быть уверены, что не имеет значения, в каком порядке вы включаете заголовки STL или Boost.

3 голосов
/ 04 мая 2010

Сначала включите заголовок, соответствующий .cpp ... другими словами, source1.cpp должен включать source1.h, прежде чем включать что-либо еще. Единственное исключение, о котором я могу подумать, - это использование MSVC с предварительно скомпилированными заголовками. В этом случае вы должны включить stdafx.h перед чем-либо еще.

Причина: Включение source1.h перед любыми другими файлами гарантирует, что он может работать автономно без своих зависимостей. Если source1.h получит зависимость на более поздний срок, компилятор немедленно предупредит вас о необходимости добавления необходимых предварительных объявлений в source1.h. Это в свою очередь гарантирует, что заголовки могут быть включены в любом порядке их иждивенцами.

Пример:

source1.h

class Class1 {
    Class2 c2;    // a dependency which has not been forward declared
};

source1.cpp

#include "source1.h"    // now compiler will alert you saying that Class2 is undefined
                    // so you can forward declare Class2 within source1.h
...

Пользователи MSVC: Я настоятельно рекомендую использовать предварительно скомпилированные заголовки. Итак, переместите все директивы #include для стандартных заголовков (и других заголовков, которые никогда не изменятся) в stdafx.h.

2 голосов
/ 04 мая 2010

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

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

0 голосов
/ 16 декабря 2011

Это сложный вопрос в мире C / C ++, с таким количеством элементов, выходящих за рамки стандарта.

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

Мои идеи таковы: если во всех этих заголовках нет конфликта символов, любой порядок в порядке, и проблему с зависимостью заголовка можно исправить позже, добавив строки #include в некорректный .h.

Реальная проблема возникает, когда какой-то заголовок меняет свое действие (проверяя условия #if) в соответствии с тем, какие заголовки находятся выше.

Например, в stddef.h в VS2005 есть:

#ifdef  _WIN64
#define offsetof(s,m)   (size_t)( (ptrdiff_t)&(((s *)0)->m) )
#else
#define offsetof(s,m)   (size_t)&(((s *)0)->m)
#endif

Теперь проблема: если у меня есть собственный заголовок ("custom.h"), который нужно использовать со многими компиляторами, включая некоторые старые, которые не предоставляют offsetof в своих системных заголовках, я должен написать в мой заголовок:

#ifndef offsetof
#define offsetof(s,m)   (size_t)&(((s *)0)->m)
#endif

И обязательно сообщите пользователю #include "custom.h" после всех системных заголовков, в противном случае строка offsetof в stddef.h выдаст ошибку переопределения макроса.

Мы молимся, чтобы больше не встречалось подобных случаев в нашей карьере.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...