Чтобы ответить на этот вопрос, нужно углубиться в процесс компиляции и то, что нужно в каждой части (вопрос, почему эти шаги выполняются, более историчен, возвращаясь к началу C до его стандартизации)
* Программы 1055 * и C ++ компилируются в несколько этапов:
- Предварительная обработка
- Компиляция
- Связывание
Предварительная обработка - это все, что начинается с #
, здесь это не очень важно.
Компиляция выполняется для каждой единицы перевода (обычно это один файл .c
или .cpp
плюс включенные в него заголовки). Компилятор берет по одному модулю перевода за раз, читает его и создает внутренний список классов и их членов, а затем код сборки каждой функции в данном модуле (на основе списка структур). Если вызов функции не является встроенным (например, он определен в другом TU), компилятор выдает «link» - «, пожалуйста, вставьте сюда функцию X », чтобы компоновщик прочитал.
Затем компоновщик берет все скомпилированные единицы перевода и объединяет их в один двоичный файл, заменяя все ссылки, указанные компилятором.
Теперь, что нужно на каждом этапе?
Для этапа компиляции вам нужно
- определение каждого класса, используемого в этом файле - компилятору необходимо знать размер и смещение каждого члена класса для создания сборки
- объявление каждой функции, используемой в этом файле - для создания этих "ссылок".
Поскольку определения функций не нужны для создания сборки (если они где-то компилируются ) ), они не нужны на этапе компиляции, только на этапе компоновки.
Подводя итог:
Существует одно правило определения для защиты программистов от этих уровней. Если они случайно определят функцию дважды, компоновщик заметит, что и исполняемый файл не создается.
Однако определения классов требуются в каждой единице перевода, и, следовательно, такое правило не может быть установлено для них. Поскольку это не может быть вызвано языком, программисты должны быть ответственными существами и не определять один и тот же класс по-разному.
ODR имеет и другие ограничения, например, вам необходимо определить функции шаблона (или класс шаблона). методы) в заголовочных файлах . Вы также можете взять на себя ответственность и сказать компилятору «Все определения этой функции будут одинаковыми, поверьте мне, чувак» и сделать функцию inline
.