В каком порядке должны быть указаны включаемые файлы, т.е. каковы причины включения одного заголовка перед другим?
Например, системные файлы, STL и Boost идут до или после локальных включаемых файлов?
В каком порядке должны быть указаны включаемые файлы, т.е. каковы причины включения одного заголовка перед другим?
Например, системные файлы, STL и Boost идут до или после локальных включаемых файлов?
Я не думаю, что есть рекомендуемый порядок, пока он компилируется! Что раздражает, так это то, что некоторые заголовки требуют, чтобы другие заголовки включались первыми... Проблема в самих заголовках, а не в порядке включений.
Мое личное предпочтение состоит в том, чтобы перейти от локального к глобальному, каждый подраздел в алфавитном порядке, то есть:
Мое обоснование для 1. состоит в том, что он должен доказать, что каждый заголовок (для которого есть cpp) может быть #include
d без предварительных условий. А все остальное кажется логически вытекающим оттуда.
Важно помнить, что ваши заголовки не должны зависеть от других заголовков, включаемых в первую очередь. Один из способов обеспечить это - включить заголовки перед любыми другими заголовками.
В частности, это упоминается в статье "Размышление в 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"
Хотя, будучи руководством, это субъективная вещь. Правила, с другой стороны, я строго соблюдаю, даже до того, что вы предоставляете заголовочные файлы "wrapper" с включенными защитными устройствами и сгруппированными, включает в себя, если какой-то неприятный сторонний разработчик не подписывается на мое видение: -)
Чтобы добавить свой кирпич к стене.
Итак, я обычно хожу так:
// 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"
Каждая группа разделяется пустой строкой следующего:
Также обратите внимание, что, помимо заголовков систем, каждый файл находится в папке с именем своего пространства имен, просто потому, что легче отслеживать их таким образом.
Я уверен, что это не рекомендуется в любом месте в здравом мире, но мне нравится, чтобы линия включала длину файла, отсортированную лексически в пределах одной длины. Например:
#include <set>
#include <vector>
#include <algorithm>
#include <functional>
Я думаю, что неплохо включить ваши собственные заголовки перед другими народами, чтобы избежать стыда зависимости от заказываемого заказа.
Я рекомендую:
И, конечно, алфавитный порядок в каждом разделе, где это возможно.
Всегда используйте форвардные объявления, чтобы избежать ненужных #include
в ваших заголовочных файлах.
Это не субъективно. Убедитесь, что ваши заголовки не полагаются на #include
d в определенном порядке. Вы можете быть уверены, что неважно, в каком порядке вы включаете заголовки 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/С++ с таким количеством элементов, которые не соответствуют стандарту.
Я думаю, что порядок заголовков файлов не является серьезной проблемой, если он компилируется, как сказал 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 будет утверждать ошибку переопределения макроса.
Мы молимся не встречаться с такими случаями в нашей карьере.