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?
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 befindetinline
. Wenn Sie dasselbe in C tun möchten, deklarieren Sie die Funktionenstatic inline
.quelle
static inline
" ... und Sie haben immer noch mehrere Kopien der Funktion in jeder Übersetzungseinheit, die sie verwendet. In C ++ ohnestatic
inline
Funktion haben Sie nur eine Kopie. Um die Implementierung tatsächlich im Header in C zu haben, müssen Sie 1) die Implementierung alsinline
(z. B. ) markiereninline void func(){do_something();}
und 2) tatsächlich angeben, dass diese Funktion in einer bestimmten Übersetzungseinheit (zvoid 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.
quelle
Sie können dies in C99 tun:
inline
Funktionen werden garantiert an einer anderen Stelle bereitgestellt. Wenn eine Funktion also nicht inline ist, wird ihre Definition in eine Deklaration übersetzt (dh die Implementierung wird verworfen). Und natürlich können Sie verwendenstatic
.quelle
C ++ - Standardzitate
Der C ++ 17 N4659-Standardentwurf 10.1.6 "Der Inline-Bezeichner" besagt, dass Methoden implizit inline sind:
und weiter unten sehen wir, dass Inline-Methoden nicht nur für alle Übersetzungseinheiten definiert werden können, sondern müssen :
Dies wird auch in einem Hinweis unter 12.2.1 "Mitgliedsfunktionen" explizit erwähnt:
Implementierung von GCC 8.3
main.cpp
Symbole kompilieren und anzeigen:
Ausgabe:
Dann sehen wir,
man nm
dass dasMyClass::myMethod
Symbol in den ELF-Objektdateien als schwach markiert ist, was bedeutet, dass es in mehreren Objektdateien auftreten kann:quelle
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.
quelle