Denn wenn Sie eine andere Datei kompilieren, muss C++ eigentlich nichts über die Implementierung wissen. Es muss nur die Unterschrift jeder Funktion (welche Parameter sie annimmt und was sie zurückgibt), den Namen jeder Klasse, welche Makros es gibt #define
d und andere "zusammenfassende" Informationen wie diese, so dass überprüft werden kann, ob Sie die Funktionen und Klassen korrekt verwenden. Der Inhalt der verschiedenen .cpp
Dateien werden erst zusammengefügt, wenn der Linker läuft.
Nehmen wir zum Beispiel an, Sie haben foo.h
int foo(int a, float b);
y foo.cpp
#include "foo.h"
int foo(int a, float b) { /* implementation */ }
y bar.cpp
#include "foo.h"
int bar(void) {
int c = foo(1, 2.1);
}
Wenn Sie kompilieren foo.cpp
wird es foo.o
, und wenn Sie kompilieren bar.cpp
wird es bar.o
. Bei der Kompilierung muss der Compiler nun prüfen, ob die Definition der Funktion foo()
en foo.cpp
deckt sich mit der Verwendung der Funktion foo()
en bar.cpp
(d.h. nimmt eine int
und eine float
und gibt eine int
). Dies geschieht dadurch, dass Sie die gleiche Header-Datei in beide .cpp
Dateien, und wenn sowohl die Definition als auch die Verwendung mit der Deklaration im Header übereinstimmen, dann müssen sie auch miteinander übereinstimmen.
Der Compiler enthält jedoch nicht die Implementierung von foo()
en bar.o
. Es enthält lediglich einen Assembler-Befehl zur call foo
. Wenn sie also eine bar.o
muss es nichts über den Inhalt von foo.cpp
. Wenn Sie jedoch zur Verknüpfungsphase kommen (die nach der Kompilierung stattfindet), muss der Linker tatsächlich über die Implementierung von foo()
denn sie wird diese Implementierung in das endgültige Programm aufnehmen und die call foo
Anweisung mit einer call 0x109d9829
(oder wie auch immer sie die Speicheradresse der Funktion foo()
sein sollte).
Beachten Sie, dass der Linker die no prüfen, ob die Implementierung von foo()
(in foo.o
) stimmt überein mit der Verwendung von foo()
(in bar.o
) - zum Beispiel prüft es nicht, ob foo()
wird aufgerufen mit einer int
und eine float
Parameter! Es ist ziemlich schwierig, diese Art von Überprüfung in Assembler durchzuführen (zumindest schwieriger als die Überprüfung des C++-Quellcodes), so dass der Linker darauf angewiesen ist, zu wissen, dass der Compiler dies bereits überprüft hat. Und deshalb braucht man die Header-Datei, um dem Compiler diese Informationen zu liefern.