Перейти к содержимому
Теория · Сборка и препроцессор · урок 1 из 8

Заголовки и единицы трансляции

Чему вы научитесь

Чему вы научитесь:

  • разносить объявления и определения по заголовкам и .cpp-файлам;
  • объяснять, какой текст #include вставляет в единицу трансляции;
  • защищать заголовок от повторного включения;
  • отличать ошибки компоновщика multiple definition и undefined reference.
Оригинал памятки triple.h и две папки main.cpp и report.cpp, в каждую вложена его копия

Представьте контору, где у каждого отдела своя папка с бумагами, а в чужие папки никто не заглядывает. Отделу продаж и бухгалтерии нужна одна и та же памятка: «функция triple принимает целое число и возвращает целое». Оригинал памятки хранят в одном месте, а в каждую папку кладут его копию. Тогда правка оригинала доходит до всех отделов.

В программе на C++ отделы — это .cpp-файлы. Функцию triple определяют в triple.cpp, а вызывают из main.cpp, report.cpp и других файлов программы. Компилятор примет каждый вызов, только если выше него стоит объявление int triple(int x);. Копировать это объявление в каждый файл вручную ненадёжно. Если тип параметра поменяют в одной копии и забудут про остальные, файлы начнут объявлять разные функции.

Заголовочный файл

Объявление пишут один раз, в заголовочном файле — файле с объявлениями, который подключают туда, где они нужны. Это и есть оригинал памятки. Файл main.cpp подключает его строкой #include. Строки, которые начинаются с #, называют директивами.

C++
#include <iostream>
#include "triple.h"

int main() {
    std::cout << triple(14) << '\n';  // 42
}

Директива #include кладёт в файл копию памятки: вставляет на своё место текст заголовка triple.h. В нём одна строка — объявление:

C++
int triple(int x);

Имя своего заголовка пишут в кавычках, а заголовка стандартной библиотеки — в угловых скобках. Заголовок обычно называют так же, как .cpp-файл с определениями. Определение остаётся в triple.cpp, и этот файл тоже подключает triple.h:

C++
#include "triple.h"

int triple(int x) {
    return x * 3;
}

Теперь #include вставляет объявление в triple.cpp, и компилятор видит его рядом с определением. Если они разойдутся по типу результата, компилятор сообщит об ошибке ещё при компиляции triple.cpp.

Единица трансляции

Компилятор переводит не отдельный файл, а единицу трансляции: .cpp-файл вместе со всем текстом, который директивы #include вставили в него. Это папка отдела вместе с вложенными копиями памяток. Каждую единицу компилятор переводит отдельно, в свой объектный файл, и в другие единицы не заглядывает.

В одном аналогия неточна: копии не хранятся в файлах. #include вставляет текст заново при каждой компиляции, а сами .cpp-файлы на диске не меняются.

Заголовок сам по себе не компилируется: его текст входит в каждую единицу, которая его подключила. Поэтому в команде сборки перечисляют только .cpp-файлы: clang++ main.cpp triple.cpp. Это команда для терминала: она компилирует перечисленные файлы и компонует их в одну программу. В Visual Studio ту же роль играет список файлов проекта.

Так программу делят на части: заголовок сообщает, какие функции есть, а .cpp-файл — как они работают. Подключить заголовок могут сколько угодно файлов программы. Объявление теперь записано в одном месте, и компилятор сверяет с ним каждый вызов.

Частая ошибка

Свой заголовок подключили в угловых скобках:

C++
#include <iostream>
#include <triple.h>  // ✗

int main() {
    std::cout << triple(14) << '\n';
}
TEXT
error: 'triple.h' file not found with <angled> include; use "quotes" instead

Это ошибка компиляции. В угловых скобках компилятор ищет заголовок среди каталогов библиотек, а папку, где лежит main.cpp, не просматривает. С кавычками поиск начинается с папки самого файла.

Главное

  • Объявления пишут в заголовке, определения — в .cpp-файле.
  • #include вставляет текст заголовка на место директивы.
  • Единица трансляции — .cpp-файл со всем вставленным текстом, и каждую компилируют отдельно.
  • Свой заголовок подключают в кавычках, заголовок стандартной библиотеки — в угловых скобках.

Подпишись, чтобы пройти курс

Курс бесплатный. Войди через Telegram и подпишись на канал — откроются все уроки.

Войти другим способом