В каком порядке должны быть указаны включаемые файлы, т.е. каковы причины включения одного заголовка перед другим?
Например, системные файлы STL и Boost идут до или после локальных включаемых файлов?
В каком порядке должны быть указаны включаемые файлы, т.е. каковы причины включения одного заголовка перед другим?
Например, системные файлы STL и Boost идут до или после локальных включаемых файлов?
Ответы:
Я не думаю, что есть рекомендуемый порядок, пока он компилируется! Что раздражает, так это то, что некоторые заголовки требуют, чтобы другие заголовки включались первыми ... Это проблема самих заголовков, а не порядка включений.
Мое личное предпочтение состоит в том, чтобы перейти от локального к глобальному, каждый подраздел в алфавитном порядке, то есть:
Мое обоснование для 1. состоит в том, что он должен доказать, что каждый заголовок (для которого есть cpp) может быть #included без предварительных условий (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"перенести в конец всех включений?
Я рекомендую:
И, конечно же, алфавитный порядок в каждом разделе, где это возможно.
Всегда используйте предварительные объявления, чтобы избежать ненужных #includes в заголовочных файлах.
Я почти уверен, что это не рекомендуемая практика нигде в здравом мире, но мне нравится, когда система включает в себя длину файла, отсортированную по лексике в той же длине. Вот так:
#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 будет сообщать об ошибке переопределения макроса.
Мы молимся, чтобы больше не встречать таких случаев в нашей карьере.