Ответ
В C++ заголовочные файлы (.h, .hpp) предназначены в первую очередь для объявлений (declarations), а исходные файлы (.cpp) — для определений (definitions, реализаций). Это разделение обусловлено моделью компиляции и линковки языка.
Основные причины:
- Нарушение правила одного определения (ODR - One Definition Rule): Если определение функции или глобальной переменной поместить в заголовочный файл, который включается в несколько единиц трансляции (
.cppфайлов), линковщик получит несколько одинаковых определений и выдаст ошибку. - Время компиляции: Каждый
.cppфайл, включающий заголовок с реализацией, будет компилировать этот код заново, что значительно увеличивает общее время сборки. - Инкапсуляция и зависимость: Изменение реализации в заголовке приводит к перекомпиляции всех файлов, которые его включают. Разделение позволяет менять реализацию в
.cpp, не затрагивая зависимые модули.
Исключения, когда код допустим/обязателен в заголовках:
- Шаблоны (templates): Их полное определение должно быть видно в точке инстанциации, поэтому обычно размещается в заголовках.
- Встраиваемые функции (
inline): Чтобы компилятор мог подставить код,inline-функции определяются в заголовке. constexpr/constevalфункции: Часто определяются в заголовках, так как могут вычисляться на этапе компиляции.- Переменные
const/constexprсо внутренней линковкой (например,static const).
Пример:
// header.h - Объявления
#ifndef MY_HEADER_H
#define MY_HEADER_H
void publicFunction(); // Объявление - правильно
int calculate(int a, int b); // Объявление
// Исключения (определения в заголовке):
inline int helper(int x) { return x * 2; } // inline-функция
template<typename T>
T max(T a, T b) { return (a > b) ? a : b; } // шаблон
#endif
// source.cpp - Определения
#include "header.h"
void publicFunction() {
// Реализация функции
int result = calculate(5, 3);
}
int calculate(int a, int b) {
return a + b + helper(a); // Используем inline-функцию из заголовка
}