В каком порядке должны быть указаны включаемые файлы, т.е. каковы причины включения одного заголовка перед другим?
Например, системные файлы STL и Boost идут до или после локальных включаемых файлов?
В каком порядке должны быть указаны включаемые файлы, т.е. каковы причины включения одного заголовка перед другим?
Например, системные файлы STL и Boost идут до или после локальных включаемых файлов?
Ответы:
Я не думаю, что есть рекомендуемый порядок, пока он компилируется! Что раздражает, так это то, что некоторые заголовки требуют, чтобы другие заголовки включались первыми ... Это проблема самих заголовков, а не порядка включений.
Мое личное предпочтение состоит в том, чтобы перейти от локального к глобальному, каждый подраздел в алфавитном порядке, то есть:
Мое обоснование для 1. состоит в том, что он должен доказать, что каждый заголовок (для которого есть cpp) может быть #include
d без предварительных условий (terminus technicus: header является «автономным»). А все остальное кажется логически вытекающим оттуда.
Важно помнить, что ваши заголовки не должны зависеть от других заголовков, включаемых в первую очередь. Один из способов обеспечить это - включить заголовки перед любыми другими заголовками.
В частности, это упоминается в статье «Мышление в C ++», ссылаясь на «Крупномасштабное проектирование программного обеспечения C ++» Лакоса:
Скрытых ошибок использования можно избежать, если обеспечить, чтобы файл .h компонента анализировался сам по себе - без предоставленных извне объявлений или определений ... Включение файла .h в качестве самой первой строки файла .c гарантирует, что не будет критической части информация, присущая физическому интерфейсу компонента, отсутствует в файле .h (или, если таковой имеется, вы узнаете об этом, как только попытаетесь скомпилировать файл .c).
То есть включить в следующем порядке:
Если у какого-либо из заголовков есть проблема с включением в этот порядок, либо исправьте их (если они у вас есть), либо не используйте их. Бойкот библиотеки, которые не пишут чистые заголовки.
Руководство по стилю Google C ++ утверждает почти обратное, безо всякого обоснования; Я лично склоняюсь в пользу подхода Лакоса.
Я следую двум простым правилам, которые избегают подавляющего большинства проблем:
Я также следую инструкциям:
Другими словами:
#include <stdio.h>
#include <string.h>
#include "btree.h"
#include "collect_hash.h"
#include "collect_arraylist.h"
#include "globals.h"
Хотя, будучи руководством, это субъективная вещь. С другой стороны, я строго соблюдаю правила, вплоть до предоставления заголовочных файлов «обертки» с включенными защитами и сгруппированными включениями, если какой-то неприятный сторонний разработчик не подпишется на мое видение :-)
Чтобы добавить мой собственный кирпич к стене.
Так что я обычно так:
// 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"
Каждая группа отделена пустой строкой от следующей:
Также обратите внимание, что, кроме системных заголовков, каждый файл находится в папке с именем своего пространства имен, просто потому, что его легче отследить таким образом.
#define
, что портит другой код), так и предотвращает неявные зависимости. Например, если наш файл заголовка базы кода foo.h
действительно зависит, <map>
но везде, где он использовался в .cc
файлах, <map>
случайно уже был включен, мы, вероятно, не заметим. Пока кто-то не попытался включить foo.h
без первого включения <map>
. И тогда они будут раздражены.
.h
есть по крайней мере один, .cpp
который включает его первым (действительно, в моем личном коде связанный с ним модульный тест сначала включает его, а исходный код включает его в свою законную группу ). Что касается отсутствия влияния, если любой из заголовков включает в себя, <map>
то все заголовки, включенные впоследствии, так или иначе будут затронуты, так что для меня это кажется проигранной битвой.
Header corresponding to this cpp file first (sanity check)
. Есть ли что-то конкретное, если #include "myproject/example.h"
перенести в конец всех включений?
Я рекомендую:
И, конечно же, алфавитный порядок в каждом разделе, где это возможно.
Всегда используйте предварительные объявления, чтобы избежать ненужных #include
s в заголовочных файлах.
Я почти уверен, что это не рекомендуемая практика нигде в здравом мире, но мне нравится, когда система включает в себя длину файла, отсортированную по лексике в той же длине. Вот так:
#include <set>
#include <vector>
#include <algorithm>
#include <functional>
Я думаю, что это хорошая идея, чтобы включить свои собственные заголовки перед другими народами, чтобы избежать позора зависимости порядка включения.
windows.h
.
Это не субъективно. Удостоверьтесь, что ваши заголовки не полагаются на то, что они находятся #include
в определенном порядке. Вы можете быть уверены, что не имеет значения, в каком порядке вы включаете заголовки STL или Boost.
Сначала включите заголовок, соответствующий .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
.
Включите от наиболее конкретного к наименее конкретному, начиная с соответствующего .hpp для .cpp, если таковой существует. Таким образом, будут обнаружены любые скрытые зависимости в заголовочных файлах, которые не являются самодостаточными.
Это осложняется использованием предварительно скомпилированных заголовков. Один из способов обойти это, не делая ваш проект компилятором, это использовать один из заголовков проекта в качестве файла скомпилированного заголовка.
Это сложный вопрос в мире 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 будет сообщать об ошибке переопределения макроса.
Мы молимся, чтобы больше не встречать таких случаев в нашей карьере.