Я использовал некоторые очень масштабные системы и никогда не видел требуемого порядка, но натолкнулся на него в последнее время. Есть ли в библиотеке STL или STD или даже Boost какие-либо случаи, когда определенные включения должны поступать в определенном порядке?
Требуется определенный порядок для #includes в С++ признак плохой библиотеки/дизайна заголовка?
Ответ 1
Имеются ли в библиотеке STL или STD или даже в Boost какие-либо случаи, когда определенные включения должны поступать в определенном порядке?
В стандарте ответ решительно: нет. Я предполагаю, что то же самое верно для Boost, хотя я не искал его.
Из стандарта C:
Стандартные заголовки могут быть включены в любом порядке; каждый из них может быть включен более одного раза в данный объем, без какого-либо эффекта, не включается только один раз, за исключением того, что эффект включения
<assert.h>
зависит от определенияNDEBUG
(см. 7.2).
стандарт С++ имеет схожую формулировку.
Мое предпочтение состоит в том, что заголовки должны включать свои собственные зависимости, но я работал с людьми, которые считают, что это "расточительно". На мой взгляд, отсутствие заголовков в их зависимостях - бесполезная ранняя оптимизация.
Ответ 2
Это определенно звучит плохой дизайн. Если каким-то образом требуется определенный порядок, библиотека должна предоставить один заголовок, который включает другие в правильном порядке.
Что касается boost и STL, я уверен, что еще не столкнулся с этой ситуацией.
Ответ 3
Необходимость указания включений в определенном порядке почти всегда указывала на проблему с дизайном. Один из способов уменьшить возможность непреднамеренно сделать это - это внедрить в практику заголовок класса как первый #include в файле реализации.
// A.cpp
#include "A.h"
#include "boost/shared_ptr.hpp"
#include <vector>
class A {
// ...
};
Таким образом, если, например, A.h использует вектор без правого #include, A.cpp не будет компилироваться.
Я не могу вспомнить, где я взял это; возможно, это был проект "Large Scale С++ Design" от Lakos (отличная книга, которая действительно может использовать обновление).
Ответ 4
Имеются ли в библиотеке STL или STD или даже в Boost какие-либо случаи, когда определенные включения должны поступать в определенном порядке?
Я никогда не сталкивался с этим, и если это так, авторы должны быть уведомлены как можно скорее. И о да, это очень плохой дизайн.
Ответ 5
Это "плохая вещь". Был упомянут лучший способ; но я уточню.
//a.h #ifndef _A_H_ #define _A_H_ //... code ... #endif // ----------------- //b.h #ifndef _B_H_ #define _B_H_ #include a.h //... code ... #endif // ----------------- //main.cpp Try 1 #include "b.h" //<- okay! b includes a, then does b // ----------------- //main.cpp Try 2 #include "a.h" //<- includes a #include "b.h" //<- okay! b includes a, but skips redefining it, then does b // ----------------- //main.cpp Try 3 #include "b.h" //<- b includes a, then does b #include "a.h" //<- okay! a skips redefining itself! // ----------------- //main.cpp Try 4 #include "a.h" //<- fail! b is not included anywhere =(
Ответ 6
Общим методом является включение заголовка совместимости уровня проекта (например, compat.h) в качестве первого заголовка любых исходных файлов .c/.cpp, который определяет кучу требуемых макросов, таких как __STDC_LIMIT_MACROS, __REENTRANT и другие макросы проекта чтобы повлиять на последующее поведение стандартных заголовков.
Я впервые увидел это использование давным-давно компетентным программистом для внутренней библиотеки. Позже я увидел, что проект git (печально известный dvcs) также использует эту технику.
Ответ 7
Если функции и/или классы, содержащиеся в заголовке (скажем, Ah), зависят от функций и/или классов, определенных в другом заголовке (например, Bh), я предпочитаю включать последнее в первое, а чтобы заставить пользователей из которых первый должен включать оба в определенном порядке.
Да:
// A.h
#pragma once
// or the #ifndef trick
#include "B.h"
// A.cpp
#include "A.h"
<Не p > Нет:
// A.h
#pragma once
// or the #ifndef trick
//#include "B.h"
// A.cpp
#include "B.h"
#include "A.h"
Ответ 8
Мне нравится включать заголовки в алфавитном порядке - упрощает просмотр того, что я уже сделал.
Если lib не будет работать, потому что он в неправильном порядке, то он сломан и должен быть исправлен, чтобы быть независимым от порядка.
Ответ 9
Для меня это плохой дизайн, который, к несчастью, оказывается в win32 API с socket/socket2 включает, если я правильно помню. Результатом является то, что ошибка в порядке включения вызовет набор ошибок, которые просто происходят из ниоткуда, и их трудно отлаживать в тех случаях, когда зависимость изменяет определения, но код все еще компилируется.
В любом другом случае вы все равно столкнетесь с проблемами. Если вы не включаете заголовок x.h, потому что y.h уже включает его, то ваш код зависит от зависимости y.h от x.h. Если позднее y.h будет реорганизован и больше не требуется y.h, удаление include приведет к поломке вашей базы кода. Это признак связи (даже если не на уровне класса): изменения в одной части базы кода должны распространяться и распространяться на другие части кода.
Ответ 10
Возможно, это знак того, что вы используете MFC, что может в свою очередь указывать на плохой дизайн (шутка... или не так ли?)
(По крайней мере, в последний раз, когда я смотрел MFC, было очень разборчиво, когда вы включили <windows.h>
)
Ответ 11
Не знаю. Это довольно плохая практика. Однако я столкнулся с этим недавно с заголовком Windows и некоторым странным интерфейсом в своем коде.
Ответ 12
Вы должны использовать include guard и forward declarations, таким образом у вас не должно быть много проблем с порядком включения заголовков.
Иногда все еще требовалось, чтобы заголовок был включен первым или последним, не знаю почему.
(Например: в исходном SDK)
Ответ 13
Да, требуя определенного порядка для включений в С++, это признак плохой архитектуры библиотеки/заголовка.
Хотя для форвардных деклараций может потребоваться более одного файла для включения, чтобы полностью использовать класс. См. Пример ниже:
//A.h
class B; // forward declaration
class A
{
void doStuff(const B& b);
};
//main.cpp
#include <A.h>
#include <B.h>
int main()
{
A a;
B b;
a.doStuff(b);
}