Подтвердить что ты не робот

Требуется определенный порядок для #includes в С++ признак плохой библиотеки/дизайна заголовка?

Я использовал некоторые очень масштабные системы и никогда не видел требуемого порядка, но натолкнулся на него в последнее время. Есть ли в библиотеке STL или STD или даже Boost какие-либо случаи, когда определенные включения должны поступать в определенном порядке?

4b9b3361

Ответ 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);
}