In C können Sie die Funktionsdefinition / -implementierung nicht in der Header-Datei haben. In C ++ können Sie jedoch eine vollständige Methodenimplementierung in der Header-Datei haben. Warum ist das Verhalten anders?
In C können Sie die Funktionsdefinition / -implementierung nicht in der Header-Datei haben. In C ++ können Sie jedoch eine vollständige Methodenimplementierung in der Header-Datei haben. Warum ist das Verhalten anders?
Antworten:
Wenn Sie in C eine Funktion in einer Header-Datei definieren, wird diese Funktion in jedem kompilierten Modul angezeigt, das diese Header-Datei enthält, und ein öffentliches Symbol wird für die Funktion exportiert. Wenn also die Funktion additup in header.h definiert ist und foo.c und bar.c beide header.h enthalten, enthalten foo.o und bar.o beide Kopien von additup.
Wenn Sie diese beiden Objektdateien miteinander verknüpfen, stellt der Linker fest, dass das Symbol additup mehrmals definiert wurde und lässt dies nicht zu.
Wenn Sie die Funktion als statisch deklarieren, wird kein Symbol exportiert. Die Objektdateien foo.o und bar.o enthalten immer noch separate Kopien des Codes für die Funktion und können diese verwenden, der Linker kann jedoch keine Kopie der Funktion sehen werde mich nicht beschweren. Natürlich kann auch kein anderes Modul die Funktion sehen. Und Ihr Programm wird mit zwei identischen Kopien der gleichen Funktion aufgebläht.
Wenn Sie die Funktion nur in der Header-Datei deklarieren, sie aber nicht definieren und dann in nur einem Modul definieren, wird dem Linker eine Kopie der Funktion angezeigt, und jedes Modul in Ihrem Programm kann sie und sehen benutze es. Und Ihr kompiliertes Programm enthält nur eine Kopie der Funktion.
Sie können also die Funktionsdefinition in der Header-Datei in C haben, es ist nur ein schlechter Stil, eine schlechte Form und eine rundum schlechte Idee.
(Mit "Deklarieren" meine ich, dass ein Funktionsprototyp ohne Textkörper angegeben wird. Mit "Definieren" meine ich, dass der tatsächliche Code des Funktionskörpers angegeben wird. Dies ist die Standard-C-Terminologie.)
#ifndef HEADER_H
verhindert werden soll?
C und C ++ verhalten sich in dieser Hinsicht sehr ähnlich - Sie können inline
Funktionen in Kopfzeilen haben. In C ++ ist implizit jede Methode enthalten, deren Rumpf sich in der Klassendefinition befindet inline
. Wenn Sie dasselbe in C tun möchten, deklarieren Sie die Funktionen static inline
.
static inline
" ... und Sie haben immer noch mehrere Kopien der Funktion in jeder Übersetzungseinheit, die sie verwendet. In C ++ ohne static
inline
Funktion haben Sie nur eine Kopie. Um die Implementierung tatsächlich im Header in C zu haben, müssen Sie 1) die Implementierung als inline
(z. B. ) markieren inline void func(){do_something();}
und 2) tatsächlich angeben, dass diese Funktion in einer bestimmten Übersetzungseinheit (z void func();
. B. ) ausgeführt wird.
Das Konzept einer Header-Datei bedarf einer kleinen Erläuterung:
Entweder geben Sie eine Datei in der Befehlszeile des Compilers ein oder Sie geben ein '#include' ein. Die meisten Compiler akzeptieren eine Befehlsdatei mit der Erweiterung c, C, cpp, c ++ usw. als Quelldatei. In der Regel enthalten sie jedoch eine Befehlszeilenoption, um die Verwendung einer beliebigen Erweiterung für eine Quelldatei zu ermöglichen.
Im Allgemeinen heißt die in der Befehlszeile angegebene Datei 'Source' und die enthaltene Datei 'Header'.
Der Präprozessor-Schritt nimmt sie alle und lässt alles für den Compiler wie eine einzige große Datei erscheinen. Was in der Kopfzeile oder in der Quelle war, ist an dieser Stelle eigentlich nicht relevant. Es gibt normalerweise eine Option eines Compilers, der die Ausgabe dieser Stufe anzeigen kann.
Für jede Datei, die in der Compiler-Befehlszeile angegeben wurde, wird dem Compiler eine große Datei übergeben. Dies kann Code / Daten enthalten, die den Speicher belegen und / oder ein Symbol erstellen, auf das aus anderen Dateien verwiesen wird. Jetzt erzeugt jeder von diesen ein 'Objekt'-Bild. Der Linker kann ein "doppeltes Symbol" angeben, wenn dasselbe Symbol in mehr als zwei Objektdateien gefunden wird, die miteinander verknüpft werden. Vielleicht ist das der Grund; Es wird nicht empfohlen, Code in eine Header-Datei einzufügen, da dies zu Symbolen in der Objektdatei führen kann.
Die 'Inline' sind normalerweise inline. Beim Debuggen werden sie jedoch möglicherweise nicht inline dargestellt. Warum gibt der Linker also keine mehrfach definierten Fehler aus? Einfach ... Dies sind "schwache" Symbole. Solange alle Daten / Codes für ein schwaches Symbol aus allen Objekten dieselbe Größe und denselben Inhalt haben, behält der Link eine Kopie und löscht Kopien von anderen Objekten. Es klappt.
C ++ - Standardzitate
Der C ++ 17 N4659-Standardentwurf 10.1.6 "Der Inline-Bezeichner" besagt, dass Methoden implizit inline sind:
4 Eine innerhalb einer Klassendefinition definierte Funktion ist eine Inline-Funktion.
und weiter unten sehen wir, dass Inline-Methoden nicht nur für alle Übersetzungseinheiten definiert werden können, sondern müssen :
6 Eine Inline-Funktion oder -Variable ist in jeder Übersetzungseinheit, in der sie verwendet wird, zu definieren und muss in jedem Fall genau dieselbe Definition haben (6.2).
Dies wird auch in einem Hinweis unter 12.2.1 "Mitgliedsfunktionen" explizit erwähnt:
1 Eine Member-Funktion kann in ihrer Klassendefinition definiert werden (11.4). In diesem Fall handelt es sich um eine Inline-Member-Funktion (10.1.6). [...]
3 [Hinweis: In einem Programm kann es höchstens eine Definition einer Nicht-Inline-Member-Funktion geben. Es kann mehr als eine Inline-Member-Funktionsdefinition in einem Programm geben. Siehe 6.2 und 10.1.6. - Endnote]
Implementierung von GCC 8.3
main.cpp
struct MyClass {
void myMethod() {}
};
int main() {
MyClass().myMethod();
}
Symbole kompilieren und anzeigen:
g++ -c main.cpp
nm -C main.o
Ausgabe:
U _GLOBAL_OFFSET_TABLE_
0000000000000000 W MyClass::myMethod()
U __stack_chk_fail
0000000000000000 T main
Dann sehen wir, man nm
dass das MyClass::myMethod
Symbol in den ELF-Objektdateien als schwach markiert ist, was bedeutet, dass es in mehreren Objektdateien auftreten kann:
"W" "w" Das Symbol ist ein schwaches Symbol, das nicht speziell als schwaches Objektsymbol gekennzeichnet wurde. Wenn ein schwach definiertes Symbol mit einem normal definierten Symbol verknüpft ist, wird das normal definierte Symbol ohne Fehler verwendet. Wenn ein schwaches undefiniertes Symbol verknüpft ist und das Symbol nicht definiert ist, wird der Wert des Symbols systemspezifisch ohne Fehler bestimmt. Bei einigen Systemen gibt Großbuchstaben an, dass ein Standardwert angegeben wurde.
Vermutlich aus demselben Grund, aus dem Sie die vollständige Methodenimplementierung in die Klassendefinition in Java einfügen müssen.
Sie sehen vielleicht ähnlich aus, mit verwinkelten Klammern und vielen der gleichen Schlüsselwörter, aber sie sind verschiedene Sprachen.