Согласно стандарту C ++:
Единица перевода является базовой единицей компиляции в C ++.Он состоит из содержимого одного исходного файла, плюс содержимое любых заголовочных файлов, прямо или косвенно включенных в него, за исключением тех строк, которые были проигнорированы с помощью операторов условной предварительной обработки.
Обратите внимание, что "косвенновключены "формулировка.Любой заголовок #include
в другом заголовке включается в любой исходный файл, который #include
содержит этот заголовок.Поэтому, если вам требуется, чтобы он был включен в заголовок, то есть, если для вашего заголовка требуется определение , а не просто объявление , #include
оно там.
AНебольшой пример того, как генерируется единица перевода: Изначально у нас есть заголовок.Давайте назовем это stdfoo.h
(это пример, вам следует избегать именования собственных заголовков с помощью std, чтобы избежать коллизий.)
#ifndef STD_FOO_ // here is our header guard
#define STD_FOO_
typedef long howl_t;
void foo();
#endif // STD_FOO_ close the header guard at the end
Давайте включим это в исходный код нашего проекта main.cxx
:
#include <stdfoo.h>
int main {
foo();
return 0;
};
Когда мы компилируем это, он запускается через препроцессор в модуль перевода.Давайте посмотрим, как может выглядеть этот сгенерированный модуль:
typedef long howl_t;
void foo();
int main {
foo();
return 0;
};
Директива #include
расширила stdfoo.h
в модуле перевода, так что компилятор может посмотреть на один модуль перевода и сгенерировать объект.
Давайте изменим все и дадим main.cxx
заголовок, main.h
.
#ifndef MAIN_H
#define MAIN_H
class BarkBark {
BarkBark() {}
void emit();
};
#endif // MAIN_H
Используйте его в нашем новом main.cxx
:
#include "main.h"
#include <stdfoo.h>
int main {
BarkBark woof;
woof.emit();
foo();
return 0;
};
Единица перевода выглядела бы следующим образом:
class BarkBark {
BarkBark() {}
void emit();
};
typedef long howl_t;
void foo();
int main {
BarkBark woof;
woof.emit();
foo();
return 0;
};
Теперь, скажем, emit
использует howl_t
в качестве аргумента, например void emit(howl_t h)
, для этого потребуется либо переопределить howl_t, рискованный способ, либо включить stdfoo.h
in main.h
#ifndef MAIN_H
#define MAIN_H
#include <stdfoo.h>
class BarkBark {
BarkBark() {}
void emit(howl_t h);
};
#endif // MAIN_H
Как выглядит этот модуль перевода?
typedef long howl_t;
void foo();
class BarkBark {
BarkBark() {}
void emit();
};
int main {
howl_t aroooooo = 0;
BarkBark woof;
woof.emit(aroooooo);
return 0;
};
Препроцессор расширил #include
внутри заголовка #include
d.
Обратите внимание, останется ли #include <stdfoo.h>
в main.cxx
, будет ли блок перевода выглядеть одинаково из-за того, что препроцессор обрабатывает защиту заголовков, второе включение просто отбрасывается.
Теперь, насколько это стандартноЗаголовки библиотек, большинство не всех стандартных библиотек защищаютполучить множественное включение, так что вы можете #include
тогда так часто и так часто, как пожелает ваше сердце без вреда для здоровья.Полученный модуль перевода включит его только один раз для компиляции.Обратите внимание, что это не гарантируется с любыми другими заголовками, и многократное включение неохраняемых заголовков может привести к очень неясным ошибкам, поскольку дубликаты объявлений существуют только в модуле перевода.определенный класс или функция, которая будет включена, #include
это в заголовке.На этом этапе это стилистический выбор, решите ли вы #include
также в исходном файле.Некоторые говорят, что делать в случае изменения заголовка, некоторые говорят, не упрощать код.Этот выбор действительно остается за вами.
Если заголовок не требуется в другом заголовочном файле, вам гораздо лучше включать его только в каждый исходный файл, который требует его, чтобы ваш код генерировал меньшие единицы перевода для компиляциии чтобы уменьшить загрязнение пространства имен.
Однако!Это не жесткое и быстрое правило.Некоторые делают это по-разному и располагают #include
в логических позициях, где они могут использоваться или где определенные заголовки используются в нескольких исходных файлах, чтобы уменьшить дублирование кода.Лично я думаю, что эта договоренность добавила технического долга, если код может быть улучшен или подвергнут рефакторингу, и заголовки станут осиротевшими.