Почему в заголовочных файлах C++ не пишут реализацию кода?

«Почему в заголовочных файлах C++ не пишут реализацию кода?» — вопрос из категории C++ Core, который задают на 25% собеседований C/C++ Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

В C++ заголовочные файлы (.h, .hpp) предназначены в первую очередь для объявлений (declarations), а исходные файлы (.cpp) — для определений (definitions, реализаций). Это разделение обусловлено моделью компиляции и линковки языка.

Основные причины:

  1. Нарушение правила одного определения (ODR - One Definition Rule): Если определение функции или глобальной переменной поместить в заголовочный файл, который включается в несколько единиц трансляции (.cpp файлов), линковщик получит несколько одинаковых определений и выдаст ошибку.
  2. Время компиляции: Каждый .cpp файл, включающий заголовок с реализацией, будет компилировать этот код заново, что значительно увеличивает общее время сборки.
  3. Инкапсуляция и зависимость: Изменение реализации в заголовке приводит к перекомпиляции всех файлов, которые его включают. Разделение позволяет менять реализацию в .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-функцию из заголовка
}