Dies ist eine dieser Arten von Codierungsstandards, die eher "sollten" als "sollen". Der Grund ist, dass Sie so ziemlich einen C ++ - Parser schreiben müssten, um ihn durchzusetzen.
Eine sehr häufige Regel für Header-Dateien ist, dass sie für sich selbst stehen müssen. Eine Header-Datei darf nicht erfordern, dass einige andere Header-Dateien # eingeschlossen werden, bevor der betreffende Header aufgenommen wird. Dies ist eine überprüfbare Anforderung. Bei einem zufälligen Header foo.hh
sollte Folgendes kompiliert und ausgeführt werden:
#include "foo.hh"
int main () {
return 0;
}
Diese Regel hat Konsequenzen hinsichtlich der Verwendung anderer Klassen in einigen Headern. Manchmal können diese Konsequenzen vermieden werden, indem diese anderen Klassen vorwärts deklariert werden. Dies ist bei vielen Standardbibliotheksklassen nicht möglich. Es gibt keine Möglichkeit, eine Vorlageninstanziierung wie std::string
oder weiterzuleiten std::vector<SomeType>
. Sie müssen #include
diese STL-Header im Header verwenden, auch wenn der Typ nur als Argument für eine Funktion verwendet wird.
Ein weiteres Problem betrifft Dinge, die Sie zufällig hineinziehen. Beispiel: Beachten Sie Folgendes:
Datei foo.cc:
#include "foo.hh"
#include "bar.hh"
void Foo::Foo () : bar() { /* body elided */ }
void Foo::do_something (int item) {
...
bar.add_item (item);
...
}
Hier bar
ist ein Klassendatenelement Foo
vom Typ Bar
. Sie haben hier das Richtige getan und #included bar.hh, obwohl dies in dem Header enthalten sein müsste, der die Klasse definiert Foo
. Sie haben jedoch die von Bar::Bar()
und verwendeten Inhalte nicht berücksichtigt Bar::add_item(int)
. Es gibt viele Fälle, in denen diese Aufrufe zu zusätzlichen externen Referenzen führen können.
Wenn Sie foo.o
mit einem Tool wie analysieren nm
, wird angezeigt, dass die Funktionen in foo.cc
alle Arten von Dingen aufrufen, für die Sie nicht die entsprechenden Schritte ausgeführt haben #include
. Sollten Sie also #include
Anweisungen für diese zufälligen externen Referenzen hinzufügen foo.cc
? Die Antwort ist absolut nicht. Das Problem ist, dass es sehr schwierig ist, die Funktionen, die zufällig aufgerufen werden, von denen zu unterscheiden, die direkt aufgerufen werden.
#include "x.h"
funktioniert, ohne dass vorhergehende erforderlich sind#include
. Das ist gut genug, wenn Sie nicht missbrauchen#define
.Wenn Sie eine Regel erzwingen müssen, dass bestimmte Header-Dateien für sich allein stehen müssen, können Sie die bereits vorhandenen Tools verwenden. Erstellen Sie ein grundlegendes Makefile, das jede Header-Datei einzeln kompiliert, aber keine Objektdatei generiert. Sie können angeben, in welchem Modus die Header-Datei kompiliert werden soll (C- oder C ++ - Modus) und überprüfen, ob sie für sich allein stehen kann. Sie können davon ausgehen, dass die Ausgabe keine Fehlalarme enthält, alle erforderlichen Abhängigkeiten deklariert sind und die Ausgabe korrekt ist.
Wenn Sie eine IDE verwenden, können Sie dies möglicherweise immer noch ohne Makefile ausführen (abhängig von Ihrer IDE). Erstellen Sie einfach ein zusätzliches Projekt, fügen Sie die Header-Dateien hinzu, die Sie überprüfen möchten, und ändern Sie die Einstellungen, um es als C- oder C ++ - Datei zu kompilieren. In MSVC würden Sie beispielsweise die Einstellung "Elementtyp" unter "Konfigurationseigenschaften-> Allgemein" ändern.
quelle
Ich glaube nicht, dass es ein solches Tool gibt, aber ich würde mich freuen, wenn eine andere Antwort mich widerlegt.
Das Problem beim Schreiben eines solchen Werkzeugs besteht darin, dass es sehr leicht ein falsches Ergebnis meldet. Daher schätze ich den Nettovorteil eines solchen Werkzeugs auf nahe Null.
Ein solches Tool kann nur funktionieren, wenn es seine Symboltabelle auf den Inhalt der von ihm verarbeiteten Header-Datei zurücksetzt. Dann tritt jedoch das Problem auf, dass die Header, die die externe API einer Bibliothek bilden, die tatsächlichen Deklarationen an delegieren interne Header.
Zum Beispiel
<string>
in der GCC - libc ++ Implementierung erklärt nicht alles, aber es enthält nur eine Reihe von internen Header - Dateien, die die eigentlichen Erklärungen enthalten. Wenn das Tool seine Symboltabelle auf das zurücksetzt, was von<string>
selbst deklariert wurde, wäre das nichts.Sie könnten das Tool zwischen
#include ""
und unterscheiden lassen#include <>
, aber das hilft Ihnen nicht, wenn eine externe Bibliothek#include ""
ihre internen Header in die API einbezieht.quelle
erreicht das nicht
#Pragma once
? Sie können etwas so oft einfügen, wie Sie möchten, entweder direkt oder über verkettete Includes. Solange sich#Pragma once
neben jedem ein Include befindet, wird der Header nur einmal eingefügt.Um dies zu erzwingen, könnten Sie vielleicht ein Build-System erstellen, das nur jeden Header für sich mit einer Dummy-Hauptfunktion enthält, um sicherzustellen, dass er kompiliert wird.
#ifdef
Kette enthält für beste Ergebnisse mit dieser Testmethode.quelle
Fügen Sie Header-Dateien immer in die CPP-Datei ein. Dies verkürzt nicht nur die Kompilierungszeit erheblich, sondern erspart Ihnen auch viel Ärger, wenn Sie sich für vorkompilierte Header entscheiden. Nach meiner Erfahrung lohnt es sich, auch nur die Mühe zu machen, Erklärungen abzugeben. Regel nur bei Bedarf brechen.
quelle
Ich würde sagen, diese Konvention hat sowohl Vor- als auch Nachteile. Einerseits ist es schön, genau zu wissen, was Ihre CPP-Datei enthält. Andererseits kann die Liste der Includes leicht zu einer lächerlichen Größe werden.
Eine Möglichkeit, diese Konvention zu fördern, besteht darin, nichts in Ihre eigenen Header aufzunehmen, sondern nur in CPP-Dateien. Dann wird jede CPP-Datei, die Ihren Header verwendet, nur kompiliert, wenn Sie explizit alle anderen Header angeben, von denen sie abhängt.
Wahrscheinlich ist hier ein vernünftiger Kompromiss angebracht. Beispielsweise können Sie entscheiden, dass es in Ordnung ist, Standardbibliotheksheader in Ihre eigenen Header aufzunehmen, jedoch nicht mehr.
quelle