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

Включая #includes в файле заголовка и исходном файле

Мне нравится помещать все мои #includes в мой заголовочный файл, а затем включать только мой заголовок для этого исходного файла в исходный файл. Что такое отраслевой стандарт? Есть ли какие-либо обратные ссылки на мой метод?

4b9b3361

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