Потому что, когда вы компилируете другой файл, C ++ на самом деле не нужно знать о реализации.Ему нужно знать только подпись каждой функции (какие параметры она принимает и что она возвращает), имя каждого класса, что такое макросы #define
d, и другую "сводную" информацию, подобную этой,так что он может проверить, что вы используете функции и классы правильно.Содержимое различных файлов .cpp
не собирается, пока не будет запущен компоновщик.
Например, скажем, у вас есть foo.h
int foo(int a, float b);
и foo.cpp
#include "foo.h"
int foo(int a, float b) { /* implementation */ }
и bar.cpp
#include "foo.h"
int bar(void) {
int c = foo(1, 2.1);
}
Когда вы компилируете foo.cpp
, он становится foo.o
, а когда вы компилируете bar.cpp
, он становится bar.o
.Теперь, в процессе компиляции, компилятор должен проверить, что определение функции foo()
в foo.cpp
согласуется с использованием функции foo()
в bar.cpp
(то есть принимает int
и float
и возвращает int
).Это достигается тем, что вы включаете один и тот же заголовочный файл в оба .cpp
файла, и если и определение, и использование согласуются с объявлением в заголовке, то они должны согласовываться друг с другом.
Но компилятор на самом деле не включает реализацию foo()
в bar.o
.Он включает в себя инструкцию на ассемблере call foo
.Поэтому, когда он создает bar.o
, ему не нужно ничего знать о содержимом foo.cpp
.Однако, когда вы переходите к этапу компоновки (который происходит после компиляции), компоновщику действительно нужно знать о реализации foo()
, потому что он собирается включить эту реализацию в конечную программу и заменить инструкцию call foo
наcall 0x109d9829
(или как он решает, адрес памяти функции foo()
должен быть).
Обратите внимание, что компоновщик не проверяет, что реализация foo()
(в foo.o
) согласен с использованием foo()
(в bar.o
) - например, он не проверяет, что foo()
вызывается с параметрами int
и float
!Довольно сложно выполнить такую проверку на ассемблере (по крайней мере, сложнее, чем проверить исходный код C ++), поэтому компоновщик полагается на знание того, что компилятор уже проверил это.И именно поэтому вам нужен заголовочный файл, чтобы предоставить эту информацию компилятору.