Требует ли определенного порядка для #include в c ++ признак плохого дизайна библиотеки / заголовка? - PullRequest
12 голосов
/ 17 декабря 2008

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

Ответы [ 13 ]

28 голосов
/ 17 декабря 2008

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

Для стандарта ответ решительно: нет . Я полагаю, что то же самое верно для Boost, хотя я не смотрел это.

Из стандарта C:

Стандартные заголовки могут быть включены в любом порядке; каждый может быть включен более одного раза в заданный объем, без эффекта отличается от включения только один раз, за ​​исключением того, что Эффект от включения <assert.h> зависит от определения NDEBUG (см. 7.2).

Стандарт C ++ имеет аналогичную формулировку.

Я предпочитаю, чтобы заголовки включали свои собственные зависимости, но я работал с людьми, которые считают это "расточительным". По моему мнению, отсутствие заголовков с их зависимостями - бесполезная ранняя оптимизация.

9 голосов
/ 17 декабря 2008

Необходимость указывать включения в конкретном порядке почти всегда указывает на проблему дизайна. Один из способов уменьшить вероятность непреднамеренного выполнения этого - начать использовать файл заголовка класса как первый #include в файле реализации.

// A.cpp
#include "A.h"
#include "boost/shared_ptr.hpp"
#include <vector>

class A {
// ...
};

Таким образом, если, например, A.h использует вектор без правильного #include, A.cpp не будет компилироваться.

Я не могу вспомнить, где я поднял это; это могло быть из «Large Scale C ++ Design» Лакоса (отличная книга, которая действительно могла бы использовать обновление).

9 голосов
/ 17 декабря 2008

Это определенно звучит как плохой дизайн. Если каким-то образом требуется определенный порядок, библиотека должна предоставить один заголовок, который включает другие в правильном порядке.

Что касается наддува и STL, я почти уверен, что еще не сталкивался с такой ситуацией.

3 голосов
/ 18 декабря 2008

Это обычная техника для включения заголовка совместимости на уровне проекта (скажем, compat.h) в качестве заголовка first любых исходных файлов .c / .cpp, который определяет набор необходимых макросов, таких как __STDC_LIMIT_MACROS, __REENTRANT и другие макросы всего проекта, влияющие на последующее поведение стандартных заголовков.

Впервые я увидел это использование компетентным программистом для внутренней библиотеки. Позже я увидел, как проект 'git' (печально известные dvcs) также использовал эту технику.

3 голосов
/ 17 декабря 2008

Это «плохая вещь». Лучший способ был упомянут; но я уточню.

//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 =(
3 голосов
/ 17 декабря 2008

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

Я никогда не сталкивался с этим, и если это так, то авторы должны быть уведомлены как можно скорее. И да, это очень плохой дизайн.

2 голосов
/ 17 декабря 2008

Если функции и / или классы, содержащиеся в заголовке (скажем, Ah), зависят от функций и / или классов, определенных в другом заголовке (например, Bh), я предпочитаю включать последний в первый, а не заставлять пользователей из первого, чтобы включить оба в определенном порядке.

Да:

// A.h
#pragma once
// or the #ifndef trick
#include "B.h"

// A.cpp
#include "A.h"
нет

№:

// A.h
#pragma once
// or the #ifndef trick
//#include "B.h"

// A.cpp
#include "B.h"
#include "A.h"
1 голос
/ 18 декабря 2008

Возможно, это признак того, что вы используете MFC, что, в свою очередь, может указывать на плохой дизайн (шутка ... или это?)

(По крайней мере, в последний раз, когда я смотрел на MFC, было очень разборчиво в отношении того, куда вы включили <windows.h>)

1 голос
/ 18 декабря 2008

Для меня это плохой дизайн, который, к сожалению, случается в Win32 API с включённым socket / socket2, если я правильно помню. В результате ошибка в порядке включения вызовет ряд ошибок, которые просто происходят из ниоткуда, и их может быть сложно отладить в тех случаях, когда зависимость меняет определения, но код по-прежнему компилируется.

В любом другом случае у вас все еще будут проблемы. Если вы не включаете заголовок x.h, потому что y.h уже включает его, то ваш код зависит от зависимости y.h от x.h. Если позднее y.h подвергнется рефакторингу, и для него больше не требуется y.h, удаление включения нарушит вашу базу кода. Это признак связывания (даже если не на уровне класса): изменения в одной части кодовой базы должны распространяться и распространяться на другие части кода.

1 голос
/ 17 декабря 2008

Мне нравится включать заголовки в алфавитном порядке - позволяет легко увидеть, что я уже сделал.

Если библиотека не работает, потому что она в неправильном порядке, то она сломана и должна быть исправлена ​​так, чтобы быть независимой от порядка.

...