Это артефакт работы компиляторов C / C ++.
Когда исходный файл компилируется, препроцессор заменяет каждый оператор # include содержимым включенного файла. Только после этого компилятор пытается интерпретировать результат этой конкатенации.
Затем компилятор просматривает этот результат от начала до конца, пытаясь проверить каждое утверждение. Если строка кода вызывает функцию, которая не была определена ранее, она сдается.
Однако существует проблема с этим, когда речь идет о взаимно рекурсивных вызовах функций:
void foo()
{
bar();
}
void bar()
{
foo();
}
Здесь foo
не будет компилироваться, поскольку bar
неизвестно. Если вы переключите две функции, bar
не скомпилируется, поскольку foo
неизвестно.
Однако если вы разделяете объявление и определение, вы можете упорядочить функции по своему усмотрению:
void foo();
void bar();
void foo()
{
bar();
}
void bar()
{
foo();
}
Здесь, когда компилятор обрабатывает foo
, он уже знает сигнатуру функции с именем bar
и счастлив.
Конечно, компиляторы могут работать по-другому, но именно так они работают в C, C ++ и в некоторой степени Objective-C.
Недостатки:
Нет напрямую. Если вы все равно используете C / C ++, это лучший способ сделать что-то. Если у вас есть выбор языка / компилятора, то, возможно, вы можете выбрать тот, где это не проблема. Единственное, что следует учитывать при разбиении объявлений на заголовочные файлы, - это избегать взаимно рекурсивных операторов # include-, но для этого предназначены защитные элементы.
Преимущества:
- Скорость компиляции: поскольку все включенные файлы объединяются, а затем анализируются, уменьшение количества и сложности кода во включаемых файлах улучшит время компиляции.
- Избегайте дублирования / вставки кода: если вы полностью определили функцию в файле заголовка, каждый объектный файл, который включает этот заголовок и ссылается на эту функцию, будет содержать свою собственную версию этой функции. Как примечание: если вы хотите вставку, вам нужно поместить полное определение в файл заголовка (на большинстве компиляторов).
- Инкапсуляция / ясность: четко определенный класс / набор функций плюс некоторая документация должны быть достаточными для того, чтобы другие разработчики могли использовать ваш код. Им (в идеале) не нужно понимать, как работает код, так зачем им просеивать его? (Контраргумент, что им может быть полезно получить доступ к реализации , когда требуется , конечно, остается в силе).
И, конечно, если вы вообще не заинтересованы в представлении функции, вы все равно можете выбрать ее полное определение в файле реализации, а не в заголовке.