Tatsache ist, dass dies in C ++ etwas komplizierter ist als die C-Header- / Quellorganisation.
Was sieht der Compiler?
Der Compiler sieht eine große Quelldatei (.cpp), deren Header ordnungsgemäß enthalten sind. Die Quelldatei ist die Kompilierungseinheit, die in eine Objektdatei kompiliert wird.
Warum sind Header notwendig?
Weil eine Kompilierungseinheit möglicherweise Informationen über eine Implementierung in einer anderen Kompilierungseinheit benötigt. So kann man beispielsweise die Implementierung einer Funktion in eine Quelle schreiben und die Deklaration dieser Funktion in eine andere Quelle schreiben, die sie verwenden muss.
In diesem Fall gibt es zwei Kopien derselben Informationen. Welches ist böse ...
Die Lösung besteht darin, einige Details zu teilen. Während die Implementierung in der Quelle verbleiben sollte, muss möglicherweise die Deklaration gemeinsamer Symbole wie Funktionen oder die Definition von Strukturen, Klassen, Aufzählungen usw. gemeinsam genutzt werden.
Überschriften werden verwendet, um diese freigegebenen Details zu platzieren.
Verschieben Sie in den Header die Deklarationen darüber, was von mehreren Quellen gemeinsam genutzt werden muss
Nichts mehr?
In C ++ gibt es einige andere Dinge, die in den Header eingefügt werden könnten, da sie ebenfalls gemeinsam genutzt werden müssen:
- Inline-Code
- Vorlagen
- Konstanten (normalerweise diejenigen, die Sie in Schaltern verwenden möchten ...)
Gehen Sie in den Header ALLES, was freigegeben werden muss, einschließlich freigegebener Implementierungen
Bedeutet das dann, dass sich Quellen in den Headern befinden könnten?
Ja. Tatsächlich gibt es viele verschiedene Dinge, die sich in einem "Header" befinden könnten (dh zwischen Quellen geteilt werden).
- Erklärungen weiterleiten
- Deklarationen / Definition von Funktionen / Strukturen / Klassen / Vorlagen
- Implementierung von Inline- und Vorlagencode
Es wird kompliziert und in einigen Fällen (zirkuläre Abhängigkeiten zwischen Symbolen) unmöglich, es in einem Header zu halten.
Überschriften können in drei Teile unterteilt werden
Dies bedeutet, dass Sie im Extremfall Folgendes haben könnten:
- ein Forward-Deklarationsheader
- ein Deklarations- / Definitionsheader
- ein Implementierungsheader
- eine Implementierungsquelle
Stellen wir uns vor, wir haben ein MyObject mit Vorlagen. Wir könnten haben:
// - - - - MyObject_forward.hpp - - - -
// This header is included by the code which need to know MyObject
// does exist, but nothing more.
template<typename T>
class MyObject ;
.
// - - - - MyObject_declaration.hpp - - - -
// This header is included by the code which need to know how
// MyObject is defined, but nothing more.
#include <MyObject_forward.hpp>
template<typename T>
class MyObject
{
public :
MyObject() ;
// Etc.
} ;
void doSomething() ;
.
// - - - - MyObject_implementation.hpp - - - -
// This header is included by the code which need to see
// the implementation of the methods/functions of MyObject,
// but nothing more.
#include <MyObject_declaration.hpp>
template<typename T>
MyObject<T>::MyObject()
{
doSomething() ;
}
// etc.
.
// - - - - MyObject_source.cpp - - - -
// This source will have implementation that does not need to
// be shared, which, for templated code, usually means nothing...
#include <MyObject_implementation.hpp>
void doSomething()
{
// etc.
} ;
// etc.
Beeindruckend!
Im "wirklichen Leben" ist es normalerweise weniger kompliziert. Der meiste Code hat nur eine einfache Header- / Quellorganisation mit etwas Inline-Code in der Quelle.
In anderen Fällen (Vorlagenobjekte, die sich kennen) musste ich für jedes Objekt separate Deklarations- und Implementierungsheader haben, wobei eine leere Quelle diese Header enthielt, damit ich einige Kompilierungsfehler erkennen konnte.
Ein weiterer Grund für die Aufteilung von Headern in separate Header könnte darin bestehen, die Kompilierung zu beschleunigen, die Anzahl der analysierten Symbole auf das erforderliche Maß zu beschränken und eine unnötige Neukompilierung einer Quelle zu vermeiden, die sich nur um die Vorwärtsdeklaration kümmert, wenn sich eine Implementierung einer Inline-Methode ändert.
Fazit
Sie sollten Ihre Code-Organisation so einfach wie möglich und so modular wie möglich gestalten. Fügen Sie so viel wie möglich in die Quelldatei ein. Stellen Sie in Headern nur fest, was freigegeben werden muss.
Aber an dem Tag, an dem Sie kreisförmige Abhängigkeiten zwischen Vorlagenobjekten haben, wundern Sie sich nicht, wenn Ihre Codeorganisation etwas "interessanter" wird als die einfache Header- / Quellorganisation ...
^ _ ^