Мне нравится помещать все мои #includes в мой заголовочный файл, а затем включать только мой заголовок для этого исходного файла в исходный файл. Что такое отраслевой стандарт? Есть ли какие-либо обратные ссылки на мой метод?
Включая #includes в файле заголовка и исходном файле
Ответ 1
Как правило, вы хотите включить минимально необходимые включения в заголовочный файл класса, поскольку все остальные, кто использует этот заголовок, будут вынуждены также #include
все из них. В более крупных проектах это ведет к более медленным построениям, проблемам зависимостей и различным видам других гадостей.
Подумайте о файле заголовка в качестве открытого интерфейса для вашего класса. Вы не хотите оседлать всех, кто использует его с дополнительными зависимостями, если только они необходимы, чтобы иметь возможность использовать класс.
Переместите все, что нужно только в реализации класса, в исходный файл. Для других классов, используемых в заголовке, только #include
их заголовки, если вам действительно нужно знать их размер или содержимое в заголовке - что угодно, и forward declaration. В большинстве случаев вам нужно только #include
классы, на которые вы наследуете, и классы, объекты которых являются членами класса вашего класса.
Эта страница содержит хорошее резюме. (Реплицируется ниже для справки)
Файл заголовка С++ включает шаблоны #
Большие программные проекты требуют тщательного управления файлами заголовков даже при программировании на C. Когда разработчики переходят на С++, управление файлами заголовков становится еще более сложным и трудоемким. Здесь мы приводим несколько шаблонов включения заголовков, которые упростят эту работу.
Правила включения заголовка заголовка
Здесь мы обсудим основные правила включения заголовочного файла С++, необходимые для упрощения управления файлами заголовков.
Заголовочный файл должен быть включен только в том случае, если декларация переадресации не будет выполнять задание.
Файл заголовка должен быть сконструирован таким образом, чтобы порядок включения заголовочного файла не был важен.
Это достигается тем, что x.h
- это первый заголовочный файл в x.cpp
Механизм включения файла заголовка должен быть толерантен к дублированию включений заголовочного файла.
В следующих разделах объясняются эти правила с помощью примера.
Пример включения файла заголовка
Следующий пример иллюстрирует различные типы зависимостей. Предположим, класс A с кодом, хранящимся в a.cpp
и a.h
.
a.h
#ifndef _a_h_included_
#define _a_h_included_
#include "abase.h"
#include "b.h"
// Forward Declarations
class C;
class D;
class A : public ABase
{
B m_b;
C *m_c;
D *m_d;
public:
void SetC(C *c);
C *GetC() const;
void ModifyD(D *d);
};
#endif
a.cpp
#include "a.h"
#include "d.h"
void A::SetC(C* c)
{
m_c = c;
}
C* A::GetC() const
{
return m_c;
}
void A::ModifyD(D* d)
{
d->SetX(0);
d->SetY(0);
m_d = d;
}
Анализ включения файлов
Давайте проанализируем включения заголовочных файлов с точки зрения классов, участвующих в этом примере, т.е. ABase
, A
, B
, C
и D
.
- Класс ABase:
ABase
является базовым классом, поэтому объявление класса требуется для завершения объявления класса. Компилятор должен знать размерABase
, чтобы определить общий размерA
. В этом случаеabase.h
должен быть явно указан вa.h
. - Класс B: Класс
A
содержит классB
по значению, поэтому объявление класса требуется для завершения объявления класса. Компилятор должен знать размер B, чтобы определить общий размерA
. В этом случаеb.h
должен быть явно указан вa.h
. - Класс C:
Class C
включен только в качестве ссылки на указатель. Размер или фактическое содержаниеC
не важны дляa.h
илиa.cpp
. Таким образом, вa.h
включено только форвардное объявление. Обратите внимание, чтоc.h
не был включен ни вa.h
, ни вa.cpp
. - Класс D: класс
D
используется как ссылка указателя вa.h
. Таким образом, достаточно прямого заявления. Ноa.cpp
использует классD
по существу, поэтому он явно включаетd.h
.
Ключевые моменты
Заголовочные файлы должны быть включены только в том случае, если форвардное объявление не будет выполнять задание. Не включая c.h
и d.h
другим клиентам класса A
никогда не придется беспокоиться о c.h
и d.h
, если они не используют класс C и D по значению.
a.h
был включен в качестве первого файла заголовка в a.cpp
. Это гарантирует, что a.h
не ожидает, что некоторые файлы заголовков будут включены до a.h
. Поскольку a.h
был включен в качестве первого файла, успешная компиляция a.cpp
гарантирует, что a.h
не ожидает, что какой-либо другой заголовочный файл будет включен до a.h
.
Если это выполняется для всех классов (т.е. x.cpp
всегда включает x.h
в качестве первого заголовка), не будет зависимости от включения заголовочного файла.
a.h
включает проверку определения препроцессора символа _a_h_included_
. Это делает его терпимым к дублированию включений a.h
.
Циклическая зависимость
Циклическая зависимость существует между классами X
и Y
в следующем примере. Эта зависимость обрабатывается с помощью форвардных объявлений.
x.h and y.h
/* ====== x.h ====== */
// Forward declaration of Y for cyclic dependency
class Y;
class X
{
Y *m_y;
...
};
/* ====== y.h ====== */
// Forward declaration of X for cyclic dependency
class X;
class Y
{
X *m_x;
...
};