C++ включает конвенции заголовка

Предположим, что у меня есть файл X.h, который определяет класс X, методы которого реализованы в X.cc. X.h файла включает файл Y.h, потому что ему нужен Y для определения класса X. В X.cc мы можем обратиться к Y, потому что X.h уже включал Y.h. Я должен все еще включать Y.h в X.cc?

Я понимаю, что я не должен, и я могу зависеть от защиты заголовка для предотвращения нескольких включений. Но с одной стороны, включая Y.h делает X.cc немного более независимый от X.h (не может быть абсолютно независимым, конечно). Какова принятая практика?

Другой пример: включая <iostream> и в.h и в .cc файлах. Я вижу, что некоторые люди делают это, и некоторые не делают.

8
задан Jon Seigel 1 April 2010 в 02:00
поделиться

7 ответов

Будьте минимальны. В заголовках предпочитайте прямые декларации полным определениям. Например, используйте iosfwd вместо ostream.

Тем не менее, X.h и X.cc представляют собой одну и ту же логическую единицу. Если ваша зависимость от Y.h когда-нибудь изменится (например, превратится в прямое объявление), вы в любом случае измените класс. Поэтому вы можете обоснованно перенести #include "Y.h" в X.cc.

Другими словами, X.cc и X.h идут рука об руку. X.cc может надежно предполагать, что находится в X.h. Поэтому нет необходимости повторно включать что-то, если это делает X.h.

Зависимости, когда вы "включаете это в любом случае", возникают с ресурсами другими, чем ваши собственные. Например, если вам нужен Z.h, вы включите его, даже если Y.h его не включает. X.h не получает возможности достоверно предполагать содержимое Y.h, потому что X.h не идет вместе с Y.h, а использует его.

10
ответ дан 5 December 2019 в 07:57
поделиться

На самом деле это не имеет большого значения [по крайней мере, если у вас нет гигантского проекта], но я, вероятно, склоняюсь к его включению.

1
ответ дан 5 December 2019 в 07:57
поделиться

Я бы предложил включить заголовок include для Y в X.cc, даже если это кажется излишним. Это дает вам преимущество быть очень явным в отношении ваших зависимостей.

В качестве сопутствующего замечания, вы всегда должны #include сопутствующий заголовок для файла cpp в качестве первого #include'd файла. (Первым включаемым файлом в X.cpp должен быть X.h) Это гарантирует, что заголовок включает соответствующие файлы для разрешения своих собственных зависимостей, иначе вы можете непреднамеренно полагаться на порядок включения в вашем исходном файле.

6
ответ дан 5 December 2019 в 07:57
поделиться

Я не уверен, почему это было бы хорошей практикой.

С другой стороны, не включение ненужных файлов в X.h - это то, что я считаю очень хорошей практикой.

Например, в следующем сценарии:

X.h

#include "Y.h"

class X
{
private:
    Y * m_pY;

public:
    X();
    ~X();
}

Достаточно переслать объявление Y . Клиенты класса X не должны нести расходы на включение файла заголовка для Y :

Xh

class Y; // include Y.h in X.cc instead

class X
{
private:
    Y * m_pY;

public:
    X();
    ~X();
}

Это возможно в файле заголовка, поскольку объявление класса X не требует конкретных сведений о Y (например, размер экземпляра); только то, что Y - это класс. Кроме того, клиенты класса X никогда не работают с типом Y , поскольку он не является частью открытого интерфейса X .

Для больших проектов отказ от ненужных директив include в файлах заголовков может значительно сократить время сборки.

Это также может избежать загрязнения пространства имен пользователей вашего класса символами из ваших частных классов / классов реализации.

3
ответ дан 5 December 2019 в 07:57
поделиться

Думаю, стоит. По мере роста вашего кода и некоторых изменений реализации, некоторые из них включают мое удаление из файла заголовка, который включается другим файлом заголовка, включенным в какой-либо файл заголовка ... Может быть очень неприятно узнать, почему что-то, что не имеет ничего общего с изменениями, внесенными вами (или кем-то еще), больше не работает.

1
ответ дан 5 December 2019 в 07:57
поделиться

В вашем примере я бы включил Xh в X.cc только для уменьшения количества включений. Да, в более общем случае, когда у вас есть A.cc, включая Xh, и в качестве побочного эффекта возможность ссылаться на материал в Yh, когда вы можете удалить Xh, вам придется вручную добавить Yh, потому что вы не добавляли его раньше . По крайней мере, компилятор будет по вашему делу и напоминать вам.

В случае он вам нужен, когда он вам нужен, будь то в файле заголовка или в модуле.

0
ответ дан 5 December 2019 в 07:57
поделиться

Операторы #include в файлах заголовков могут увеличить время компиляции. Для игрушечных приложений или больших систем с одной зависимостью, как вы описываете, это не будет проблемой. Но по мере роста вашего приложения время компиляции будет увеличиваться. Одно из решений MSVC - использовать предварительно скомпилированные заголовки (которые приносят собственный набор проблем) или исключить все операторы #include в заголовках. Последний - мой подход по умолчанию, и я сначала пытаюсь использовать форвардные объявления там, где это необходимо:

yh:

class Y {public: int foo_; };

xh:

class Y;
class X { public: Y* y_; };

main.cc:

#include "y.h"
#include "x.h"

int main()
{
  X x;
  return 0;
}

Если использование форвардных объявлений невозможно, просто помните, что #include не делает ничего, кроме переноса содержимого указанного файла в эту точку, и это можно сделать так же легко из файла cc, как и из заголовка:

yh:

class Y {public: int foo_; };

xh:

class X {public: Y y_; }; // note this declaration requires a concrete type for Y

main.cc:

#include "y.h"
#include "x.h"

int main()
{
  X x;
  return 0;
}
0
ответ дан 5 December 2019 в 07:57
поделиться
Другие вопросы по тегам:

Похожие вопросы: