Мне нравится помещать все мои #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;
...
};