Teil 2. Header-Dateien
...
Beim Schreiben von Code verwenden wir alle die Code-Formatierungsregeln. Manchmal werden Regeln erfunden, in anderen Fällen werden vorgefertigte Styleguides verwendet. Obwohl alle C ++ - Programmierer Englisch leichter lesen als Muttersprachler, ist es besser, ein Handbuch in letzterem zu haben.
Dieser Artikel ist eine Übersetzung eines Teils des C ++ - Styleguides von Google ins Russische.
Originalartikel (Gabel auf Github), aktualisierte Übersetzung .
Header-Dateien
Es ist wünschenswert, dass jede .cc- Quelldatei eine übereinstimmende .h- Headerdatei hat. Es sind auch Ausnahmen von dieser Regel bekannt, z. B. Komponententests oder kleine .cc- Dateien, die nur die Funktion main () enthalten .
Die korrekte Verwendung von Header-Dateien kann einen großen Einfluss auf die Lesbarkeit, Größe und Leistung Ihres Codes haben.
Die folgenden Regeln helfen Ihnen, häufige Probleme mit Header-Dateien zu vermeiden.
Unabhängige Header-Dateien
Header-Dateien müssen autark sein (in Bezug auf die Kompilierung) und die Erweiterung .h haben . Andere (Nicht-Header-) Dateien, die in den Code aufgenommen werden sollen, müssen die Erweiterung .inc haben und mit dem Include-Code gekoppelt sein.
Alle Header-Dateien sollten in sich geschlossen sein. Benutzer und Entwicklungstools sollten bei der Verwendung der Header-Datei nicht von speziellen Abhängigkeiten abhängig sein. Die Header-Datei muss abschließbar sein und alle erforderlichen Dateien enthalten.
Es ist vorzuziehen, Definitionen für Vorlagen und Inline-Funktionen mit ihren Deklarationen in derselben Datei zu platzieren. Und diese Definitionen müssen in jeder .cc enthalten seinDatei, die sie verwendet, andernfalls können bei einigen Build-Konfigurationen Verknüpfungsfehler auftreten. Wenn sich die Deklarationen und Definitionen in verschiedenen Dateien befinden, sollte eine die andere enthalten. Trennen Sie Definitionen nicht in separate Header-Dateien ( -inl.h ). Früher war diese Praxis sehr beliebt, jetzt ist sie unerwünscht.
Wenn ausnahmsweise alle verfügbaren Varianten von Vorlagenargumenten aus der Vorlage erstellt werden oder wenn die Vorlage Funktionen implementiert, die nur von einer Klasse verwendet werden, ist es zulässig, die Vorlage in einer (und nur einer) .cc- Datei zu definieren, in der diese Vorlage verwendet wird.
Es gibt seltene Situationen, in denen die Header-Datei nicht autark ist. Dies kann passieren, wenn eine Datei an einem nicht standardmäßigen Speicherort enthalten ist, z. B. in der Mitte einer anderen Datei. In diesem Fall gibt es möglicherweise keine Sperre für die erneute Aktivierung , und zusätzliche Header-Dateien sind möglicherweise auch nicht enthalten. Benennen Sie solche Dateien mit der Erweiterung .inc . Verwenden Sie sie paarweise und versuchen Sie, die allgemeinen Anforderungen so gut wie möglich zu erfüllen.
Sperre neu starten
Alle Header-Dateien müssen #define gegen Wiedereingliederung geschützt sein . Das Makrodefinitionsformat sollte sein: <PROJEKT> _ <PATH> _ <DATEI> _H_ .
Verwenden Sie die vollständigen Dateipfadkomponenten im Projektbaum, um die Eindeutigkeit sicherzustellen. Beispielsweise kann die Datei foo / src / bar / baz.h im Projekt foo die folgende Sperre haben:
#ifndef FOO_BAR_BAZ_H_
#define FOO_BAR_BAZ_H_
...
#endif // FOO_BAR_BAZ_H_
Vorläufige Ankündigung
Verwenden Sie nach Möglichkeit keine Vorankündigungen. # Schließen Sie stattdessen die erforderlichen Header-Dateien ein .
Definition
"Vordeklaration" ist eine Deklaration einer Klasse, Funktion, Vorlage ohne entsprechende Definition.
Pro
- . #include ( ) .
- . #include - .
- , .
- API, . , API: , , .
- std:: .
- , : #include. , #include ( ) :
// b.h: struct B {}; struct D : B {}; // good_user.cc: #include "b.h" void f(B*); void f(void*); void test(D* x) { f(x); } // calls f(B*)
#include B D, test() f(void*). - , .
- Eine Codestruktur, die eine vorläufige Deklaration ermöglicht (und außerdem Zeiger als Klassenmitglieder verwendet), kann den Code verwirrend und langsam machen.
Urteil
- Vermeiden Sie es, Entitäten, die in einem anderen Projekt deklariert wurden, vorab zu deklarieren.
- Bei der Verwendung einer Funktion in einer Header - Datei deklariert, immer # include diese Datei.
- Bei Verwendung einer Klassenvorlage ist es vorzuziehen, die Header-Datei einzuschließen .
Siehe auch die Regeln für die Aufnahme in Namen und die Einschlussreihenfolge .
Inline-Funktionen
Definieren Sie Funktionen nur dann als Inline-Funktionen, wenn sie klein sind, z. B. nicht mehr als 10 Zeilen.
Definition
Sie können Funktionen als Inline-Funktionen deklarieren und den Compiler anweisen, sie zusätzlich zur Standardmethode zum Aufrufen einer Funktion direkt in den aufrufenden Code aufzunehmen.
Vorteile Die
Verwendung von Inline-Funktionen kann effizienteren Code generieren, insbesondere wenn die Funktionen klein sind. Verwenden Sie diese Funktion für Get / Set-Funktionen, andere kurze und leistungskritische Funktionen.
Gegen
Übermäßiger Gebrauch von Inline-Funktionen kann das Programm verlangsamen. Außerdem können Inline-Funktionen je nach Größe den Code vergrößern oder verkleinern. Wenn dies kleine Funktionen sind, kann der Code minimiert werden. Wenn die Funktion groß ist, kann die Codegröße sehr stark zunehmen. Beachten Sie, dass auf modernen Prozessoren schlankerer Code aufgrund der besseren Verwendung des Anweisungscaches schneller ausgeführt wird.
Urteil
Eine gute Faustregel ist, Funktionen nicht inlinierbar zu machen, wenn sie 10 Codezeilen überschreiten. Vermeiden Sie es, Destruktoren inlinierbar zu machen, da Sie können implizit viel zusätzlichen Code enthalten: Aufrufe von variablen Destruktoren und Basisklassen!
Eine weitere gute Faustregel ist, dass Inline-Funktionen mit Schleifen oder switch-Anweisungen normalerweise nicht sinnvoll sind (außer in entarteten Fällen, in denen die Schleife oder andere Anweisungen niemals ausgeführt werden).
Es ist wichtig zu verstehen, dass eine Inline-Funktion nicht unbedingt auf diese Weise in Code kompiliert wird. Beispielsweise werden normalerweise virtuelle und rekursive Funktionen mit einem Standardaufruf kompiliert. Im Allgemeinen sollten rekursive Funktionen nicht als Inline-Funktionen deklariert werden. Der Hauptgrund für die Ausführung virtueller Inline-Funktionen besteht darin, die Definition (den Code) in die Klassendefinition selbst einzufügen (um das Verhalten oder die Lesbarkeit zu dokumentieren) - häufig für Get / Set-Funktionen verwendet.
Namen und Reihenfolge einschließen
Fügen Sie die Header-Dateien in der folgenden Reihenfolge ein: gepaarte Datei (z. B. foo.h - foo.cc), C-Systemdateien, C ++ - Standardbibliothek, andere Bibliotheken, Ihre Projektdateien.
Alle Projektheader müssen relativ zum Projektquellverzeichnis sein, ohne UNIX-Aliase wie z . (aktuelles Verzeichnis) oder .. (übergeordnetes Verzeichnis). Zum Beispiel sollte google-awesome-project / src / base / logging.h wie folgt enthalten sein:
#include "base/logging.h"
Ein weiteres Beispiel: Wenn die Hauptfunktion der Dateien dir / foo.cc und dir / foo_test.cc darin besteht, den in dir2 / foo2.h deklarierten Code zu implementieren und zu testen , schreiben Sie die Header-Dateien in der folgenden Reihenfolge:
- dir2 / foo2.h .
- —
- C (: .h), <unistd.h>, <stdlib.h>.
- —
- C++ ( ), <algorithm>, <cstddef>.
- —
- .h .
- .h .
Trennen Sie jede (nicht leere) Gruppe von Dateien durch eine leere Zeile.
Mit dieser Reihenfolge von Dateien können Sie Fehler erkennen, wenn die erforderlichen Header-Dateien (System usw.) in der gepaarten Header-Datei ( dir2 / foo2.h ) fehlen und die Zusammenstellung der entsprechenden dir / foo.cc- oder dir / foo_test.cc-Dateien fehlschlägt. Infolgedessen wird der Fehler sofort dem Entwickler angezeigt, der mit diesen Dateien arbeitet (und nicht einem anderen Team, das nur eine externe Bibliothek verwendet).
Normalerweise befinden sich die gepaarten Dateien dir / foo.cc und dir2 / foo2.h im selben Verzeichnis (z. B. base / basictypes_test.cc und base / basictypes.h ), obwohl dies nicht erforderlich ist.
Beachten Sie, dass C-Header-Dateien wie stddef.h normalerweise mit den entsprechenden C ++ - Dateien ( cstddef ) austauschbar sind . Jede Variation kann verwendet werden, es ist jedoch am besten, dem Stil des vorhandenen Codes zu folgen.
In jedem Abschnitt werden die Header-Dateien am besten in alphabetischer Reihenfolge aufgelistet. Beachten Sie, dass zuvor geschriebener Code möglicherweise nicht dieser Regel folgt. Wenn möglich (z. B. beim Korrigieren einer Datei), korrigieren Sie die Reihenfolge der Dateien auf die richtige.
Alle Header-Dateien, die die gewünschten Typen deklarieren, sollten enthalten sein, außer wenn sie zuvor deklariert wurden . Wenn Ihr Code Typen aus bar.h verwendet , verlassen Sie sich nicht auf eine andere Datei foo.h, um bar.h einzuschließenund Sie können sich darauf beschränken, nur foo.h einzuschließen : explizit bar.h einschließen (es sei denn, es wird ausdrücklich (möglicherweise in der Dokumentation) angegeben, dass foo.h Ihnen auch die Typen von bar.h gibt ).
Die Liste der Header-Dateien in google-awesome-project / src / foo / internal / fooserver.cc könnte beispielsweise folgendermaßen aussehen:
#include "foo/server/fooserver.h"
#include <sys/types.h>
#include <unistd.h>
#include <string>
#include <vector>
#include "base/basictypes.h"
#include "base/commandlineflags.h"
#include "foo/server/bar.h"
Ausnahmen
Es gibt Fälle, in denen Sie Header-Dateien abhängig von den Bedingungen des Präprozessors einschließen müssen (z. B. abhängig vom verwendeten Betriebssystem). Versuchen Sie, diese Aufnahme so kurz (lokalisiert) wie möglich zu halten und sie nach anderen Header-Dateien zu platzieren. Zum Beispiel:
#include "foo/public/fooserver.h"
#include "base/port.h" // For LANG_CXX11.
#ifdef LANG_CXX11
#include <initializer_list>
#endif // LANG_CXX11
Anmerkungen:
Bild aus Open Source .