it-swarm.com.ru

C / C ++ включает порядок заголовочных файлов

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

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

247
Anycorn

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

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

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

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

253
squelart

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

В частности, это упоминается в статье "Мышление в 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++ утверждает почти наоборот, безо всякого оправдания; Я лично склоняюсь в пользу подхода Лакоса.

86
Nathan Paul Simons

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

  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"

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

48
paxdiablo

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

  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 первым (проверка работоспособности)
  • Системные заголовки
  • Сторонние заголовки, организованные по порядку зависимости
  • Заголовки проекта
  • Частные заголовки проекта

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

20
Matthieu M.

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

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

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

14
msandiford

Рекомендую:

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

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

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

13
i_am_jorf

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

6
wilhelmtell

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

Reasoning: Включение 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.

3
Agnel Kurian

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

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

2
dcw

Это сложный вопрос в мире 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 выдаст ошибку переопределения макроса.

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

0
Jimm Chen