Wo sollen Include-Anweisungen, Header oder Quellen abgelegt werden?


106

Soll ich die Includes in die Header-Datei oder die Quelldatei einfügen? Wenn die Header-Datei die include-Anweisungen enthält, wenn ich diese Header-Datei in meine Quelle einbinde, enthält meine Quelldatei dann alle enthaltenen Dateien, die sich in meinem Header befanden? Oder sollte ich sie nur in meine Quelldatei aufnehmen?


2
Viele frühere Duplikate auf SO, z. B. wo "enthalten" sollte in C ++
Paul R

Antworten:


141

Fügen Sie Includes nur dann in einen Header ein, wenn der Header sie selbst benötigt.

Beispiele:

  • Ihre Funktion gibt den Typ zurück size_t. Dann #include <stddef.h>in der Header- Datei.
  • Ihre Funktion verwendet strlen. Dann #include <string.h>in der Quelldatei .

2
Was ist, wenn meine Funktion ein Argument vom Typ size_takzeptiert?
Andrybak

Dieselbe Frage, die sich auf c ++ erweitert: Was ist, wenn meine Struktur / Klasse ein Feld / Mitglied vom Typ size_toder hat std::string?
Andrybak

10
Was ist die Begründung?
Patrizio Bertoni

Ich habe eine verdrahtete Situation, die C ++ - Klasse A hat ein Objekt einer anderen Klasse B, und ich kann keine Vorwärtsdeklaration von B und End-up einschließlich B-Header in A-Header verwenden. (mit Zeiger hat dieses Problem nicht)
Shuva

@andrybak Ihre Quelldateien sollten Ihre Header-Datei enthalten, damit alle, die Ihren Header enthalten, auch Ihre Quelle erhalten.
Jeremy Trifilo

27

Im Laufe der Jahre gab es einige Meinungsverschiedenheiten darüber. Zu einer Zeit war es traditionell so, dass ein Header nur deklarierte, in welchem ​​Modul er sich befand. So viele Header hatten bestimmte Anforderungen, dass Sie #includeeinen bestimmten Satz von Headern (in einer bestimmten Reihenfolge) haben. Einige extrem traditionelle C-Programmierer folgen immer noch diesem Modell (zumindest in einigen Fällen religiös).

In jüngerer Zeit gibt es eine Tendenz, die meisten Header eigenständig zu machen. Wenn dieser Header etwas anderes erfordert, übernimmt der Header selbst dies und stellt sicher, dass alles, was er benötigt, enthalten ist (in der richtigen Reihenfolge, wenn es Bestellprobleme gibt). Persönlich bevorzuge ich dies - besonders wenn die Reihenfolge der Überschriften wichtig sein kann, löst es das Problem einmal, anstatt dass jeder, der es verwendet, das Problem noch einmal lösen muss.

Beachten Sie, dass die meisten Header nur Deklarationen enthalten sollten. Dies bedeutet, dass das Hinzufügen eines unnötigen Headers (normalerweise) keine Auswirkungen auf Ihre endgültige ausführbare Datei haben sollte. Das Schlimmste, was passiert, ist, dass es die Kompilierung etwas verlangsamt.


2
Wenn alle Header im zweiten Stil geschrieben sind, sollte es überhaupt keine Bestellprobleme geben. Bestellprobleme in den Kopfzeilen bedeuten normalerweise, dass Sie nicht alles, was Sie brauchen, in die Kopfzeile aufgenommen haben.
Auf Wiedersehen SE

12

Ihr #includes sollte aus Header-Dateien bestehen, und jede Datei (Quelle oder Header) sollte #includedie Header-Dateien enthalten, die sie benötigt. Header-Dateien sollten #includedie minimal erforderlichen Header-Dateien enthalten, und Quelldateien sollten dies auch tun, obwohl dies für Quelldateien nicht so wichtig ist.

Die Quelldatei enthält die Header #includeund die Header #includeusw. bis zur maximalen Verschachtelungstiefe. Aus diesem Grund möchten Sie keine überflüssigen #includes in Header-Dateien: Sie können dazu führen, dass eine Quelldatei viele Header-Dateien enthält, die möglicherweise nicht benötigt werden, was die Kompilierung verlangsamt.

Dies bedeutet, dass es durchaus möglich ist, dass Header-Dateien zweimal enthalten sind, und das kann ein Problem sein. Die traditionelle Methode besteht darin, "Include Guards" in Header-Dateien einzufügen, z. B. für die Datei foo.h:

#ifndef INCLUDE_FOO_H
#define INCLUDE_FOO_H
/* everything in header goes here */
#endif

Ich weiß, dass diese Antwort super alt ist, aber seitdem haben sie #pragma einmal hinzugefügt, sodass Sie nicht #ifndef einschließen müssen, wenn Sie #includes deklarieren
Dogunbound Hounds

6

Der Ansatz, zu dem ich mich in über zwanzig Jahren entwickelt habe, ist dieser;

Betrachten Sie eine Bibliothek.

Es gibt mehrere C-Dateien, eine interne H-Datei und eine externe H-Datei. Die C-Dateien enthalten die interne H-Datei. Die interne H-Datei enthält die externe H-Datei.

Sie sehen, dass aus dem POV des Compilers beim Kompilieren einer C-Datei eine Hierarchie besteht.

extern -> intern -> C-Code

Dies ist die richtige Reihenfolge, da das, was extern ist, alles ist, was ein Dritter benötigt, um die Bibliothek zu nutzen. Das Interne ist erforderlich, um den C-Code zu kompilieren.


4

Wenn Header - Datei Eine #includesHeader - Dateien B und C, so ist jede Quelldatei , dass #includesA auch B und C erhalten #included. Der Vorprozessor führt buchstäblich nur eine Textersetzung durch: überall dort, wo er Text findet, der besagt, #include <foo.h>dass er durch den Text der foo.hDatei ersetzt wird.

Es gibt unterschiedliche Meinungen darüber, ob Sie #includesHeader oder Quelldateien einfügen sollten. Persönlich ziehe ich es vor, alle #includesstandardmäßig in die Quelldatei aufzunehmen, aber alle Header-Dateien, die ohne andere vorausgesetzte Header nicht kompiliert werden können, sollten #includediese Header selbst verwenden.

Und jede Header-Datei sollte einen Include-Guard enthalten, um zu verhindern, dass sie mehrmals eingeschlossen wird.


4

In einigen Umgebungen ist die Kompilierung am schnellsten, wenn nur die benötigten Header-Dateien enthalten sind. In anderen Umgebungen wird die Kompilierung optimiert, wenn alle Quelldateien dieselbe primäre Sammlung von Headern verwenden können (einige Dateien enthalten möglicherweise zusätzliche Header, die über die gemeinsame Teilmenge hinausgehen). Im Idealfall sollten Header so erstellt werden, dass mehrere # include-Operationen keine Auswirkung haben. Es kann sinnvoll sein, # include-Anweisungen mit Überprüfungen des Include-Schutzes der einzuschließenden Datei zu umgeben, obwohl dies eine Abhängigkeit vom Format dieses Schutzes schafft. Abhängig vom Dateicaching-Verhalten eines Systems kann es außerdem nicht lange dauern, bis ein unnötiger # Include, dessen Ziel am Ende vollständig # ifdef'ed ist, entfernt wird.

Eine andere zu berücksichtigende Sache ist, dass, wenn eine Funktion einen Zeiger auf eine Struktur nimmt, man den Prototyp als schreiben kann

void foo (struct BAR_s * bar);

ohne dass eine Definition für BAR_s im Gültigkeitsbereich liegen muss. Ein sehr praktischer Ansatz zur Vermeidung unnötiger Einschlüsse.

PS: In vielen meiner Projekte wird es eine Datei geben, von der erwartet wird, dass jedes Modul #include enthält, die Dinge wie typedefs für ganzzahlige Größen und einige gemeinsame Strukturen und Vereinigungen enthält [z

typedef union {
  unsigned long l;
  unsigned short lw [2];
  vorzeichenloses Zeichen lb [4];
} U_QUAD;

(Ja, ich weiß, dass ich in Schwierigkeiten geraten würde, wenn ich zu einer Big-Endian-Architektur wechseln würde. Da mein Compiler jedoch keine anonymen Strukturen in Unions zulässt, muss für die Verwendung benannter Bezeichner für die Bytes innerhalb der Union auf sie zugegriffen werden theUnion.b.b1 usw., was ziemlich nervig erscheint.


3

Erstellen Sie alle Ihre Dateien so, dass sie nur mit dem erstellt werden können, was sie enthalten. Wenn Sie kein Include in Ihrem Header benötigen, entfernen Sie es. Wenn Sie in einem großen Projekt diese Disziplin nicht beibehalten, können Sie einen gesamten Build brechen, wenn jemand ein Include aus einer Header-Datei entfernt, die von einem Konsumenten dieser Datei und nicht einmal vom Header verwendet wird.


1

Ihre Quelldatei enthält die include-Anweisungen, wenn Sie sie in den Header einfügen. In einigen Fällen ist es jedoch besser, sie in die Quelldatei einzufügen.

Denken Sie daran, dass, wenn Sie diesen Header in andere Quellen aufnehmen, diese auch die Includes aus dem Header erhalten, was nicht immer wünschenswert ist. Sie sollten nur Dinge einschließen, in denen sie verwendet werden.


1

Sie sollten nur Dateien in Ihren Header aufnehmen, die Sie zum Deklarieren von Konstanten und Funktionsdeklarationen benötigen. Technisch gesehen sind diese Includes auch in Ihrer Quelldatei enthalten. Aus Gründen der Übersichtlichkeit sollten Sie jedoch nur die Dateien in jede Datei aufnehmen, die Sie tatsächlich verwenden müssen. Sie sollten sie auch in Ihrem Header vor mehrfacher Aufnahme schützen:

#ifndef NAME_OF_HEADER_H
#define NAME_OF_HEADER_H

...definition of header file...

#endif

Dies verhindert, dass der Header mehrmals eingeschlossen wird, was zu einem Compilerfehler führt.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.