Was sollte in eine .h-Datei kommen?

Lesezeit: 9 Minuten

Was sollte in eine h Datei kommen
Enrico Tuvera jr

Wenn Sie Ihren Code in mehrere Dateien aufteilen, was genau sollte in eine .h-Datei und was in eine .cpp-Datei aufgenommen werden?

  • Verwandte Frage: stackoverflow.com/questions/333889/…

    – Sprich

    22. Dezember 2009 um 11:44 Uhr

  • Dies ist ein reines Stilproblem, aber ich glaube, dass C++-Deklarationen in a gehen .hpp Datei, während C-Deklarationen in a gehen .h Datei. Dies ist sehr hilfreich beim Mischen von C- und C++-Code (z. B. Legacy-Module in C).

    – Thomas Matthäus

    22. Dezember 2009 um 13:29 Uhr

  • @ThomasMatthews Macht Sinn. Wird diese Praxis oft angewendet?

    – ty

    22. Mai 2018 um 1:42 Uhr

  • @lightningleaf: Ja, die Praxis wird häufig angewendet, insbesondere beim Mischen von C++- und C-Sprachen.

    – Thomas Matthäus

    22. Mai 2018 um 14:06 Uhr

Header-Dateien (.h) wurden entwickelt, um die Informationen bereitzustellen, die in mehreren Dateien benötigt werden. Dinge wie Klassendeklarationen, Funktionsprototypen und Aufzählungen gehen normalerweise in Header-Dateien. Mit einem Wort „Definitionen“.

Codedateien (.cpp) sollen die Implementierungsinformationen bereitstellen, die nur in einer Datei bekannt sein müssen. Im Allgemeinen gehören Funktionskörper und interne Variablen, auf die niemals von anderen Modulen zugegriffen werden soll/wird, hinein .cpp Dateien. Mit einem Wort, “Implementierungen”.

Die einfachste Frage, die Sie sich stellen müssen, um festzustellen, was wohin gehört, lautet: “Wenn ich dies ändere, muss ich dann den Code in anderen Dateien ändern, damit die Dinge wieder kompiliert werden?” Wenn die Antwort “Ja” ist, gehört sie wahrscheinlich in die Header-Datei; Wenn die Antwort “nein” ist, gehört es wahrscheinlich in die Codedatei.

  • Außer private Klassendaten müssen in den Header gehen. Vorlagen müssen vollständig Header-definiert sein (es sei denn, Sie verwenden einen der wenigen Compiler, der unterstützt export). Der einzige Weg, Nr. 1 zu umgehen, ist PIMPL. #2 wäre möglich wenn export wurde unterstützt und ist möglicherweise mit c++0x und möglich extern Vorlagen. IMO, Header-Dateien in c++ verlieren viel von ihrer Nützlichkeit.

    – KitsuneYMG

    22. Dezember 2009 um 12:39 Uhr

  • Alles gut, aber mit ungenauer Terminologie. Mit einem Wort „Erklärungen“ – der Begriff „Definition“ ist gleichbedeutend mit „Umsetzung“. Nur deklarativer Code, Inline-Code, Makrodefinitionen und Vorlagencode sollten sich in einem Header befinden; dh nichts, was Code oder Daten instanziiert.

    – Clifford

    22. Dezember 2009 um 13:44 Uhr

  • Da muss ich Clifford zustimmen. Sie verwenden die Begriffe Deklaration und Definition ziemlich locker und etwas austauschbar. Aber sie haben genaue Bedeutungen in C++. Beispiele: Eine Klassendeklaration führt den Namen einer Klasse ein, sagt aber nicht, was darin enthalten ist. Eine Klassendefinition listet alle Member und Friend-Funktionen auf. Beide können problemlos in Header-Dateien gesteckt werden. Was Sie “Funktionsprototyp” nennen, ist eine Funktion Erklärung. Sondern eine Funktion Definition ist das Ding, das den Code der Funktion enthält und in einer cpp-Datei platziert werden sollte — es sei denn, es ist inline oder (Teil eines) Templates.

    – Sellibitze

    22. Dezember 2009 um 16:22 Uhr

  • Sie haben eine genaue Bedeutung in C++, sie haben keine genaue Bedeutung im Englischen. Meine Antwort wurde in letzterem geschrieben.

    – Bernstein

    22. Dezember 2009 um 21:53 Uhr

1646940012 840 Was sollte in eine h Datei kommen
paercebal

Tatsache ist, dass dies in C++ etwas komplizierter ist als die C-Header/Quell-Organisation.

Was sieht der Compiler?

Der Compiler sieht eine große Quelldatei (.cpp), deren Header korrekt enthalten sind. Die Quelldatei ist die Kompilierungseinheit, die in eine Objektdatei kompiliert wird.

Also, warum sind Header notwendig?

Weil eine Übersetzungseinheit Informationen über eine Implementierung in einer anderen Übersetzungseinheit benötigen könnte. So kann man zum Beispiel die Implementierung einer Funktion in einer Quelle schreiben und die Deklaration dieser Funktion in einer anderen Quelle schreiben, um sie zu verwenden.

In diesem Fall gibt es zwei Kopien derselben Informationen. Was ist böse…

Die Lösung besteht darin, einige Details zu teilen. Während die Implementierung in der Quelle bleiben sollte, könnte die Deklaration gemeinsam genutzter Symbole wie Funktionen oder die Definition von Strukturen, Klassen, Aufzählungen usw. gemeinsam genutzt werden müssen.

Header werden verwendet, um diese freigegebenen Details zu platzieren.

Verschieben Sie die Erklärungen dessen, was zwischen mehreren Quellen geteilt werden muss, in die Kopfzeile

Nichts mehr?

In C++ gibt es einige andere Dinge, die in den Header eingefügt werden könnten, da sie ebenfalls geteilt werden müssen:

  • Inline-Code
  • Vorlagen
  • Konstanten (normalerweise diejenigen, die Sie in Schaltern verwenden möchten …)

Verschieben Sie in die Kopfzeile ALLES, was geteilt werden muss, einschließlich geteilter Implementierungen

Bedeutet das dann, dass es Quellen in den Headern geben könnte?

Jawohl. 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/Templates
  • Implementierung von Inline- und Template-Code

Es wird kompliziert und in einigen Fällen (zirkuläre Abhängigkeiten zwischen Symbolen) unmöglich, es in einem Header zu halten.

Kopfzeilen können in drei Teile unterteilt werden

Das 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 vorlagenbasiertes MyObject. 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 “echten Leben” ist es meist weniger kompliziert. Der meiste Code wird nur eine einfache Header-/Quellorganisation haben, mit etwas eingebettetem Code in der Quelle.

Aber in anderen Fällen (Vorlagenobjekte kennen sich gegenseitig) musste ich für jedes Objekt separate Deklarations- und Implementierungsheader haben, mit einer leeren Quelle, die diese Header enthielt, nur um mir zu helfen, einige Kompilierungsfehler zu sehen.

Ein weiterer Grund, Header in separate Header aufzuteilen, könnte darin bestehen, die Kompilierung zu beschleunigen, die Menge der geparsten Symbole auf das unbedingt Notwendige zu beschränken und unnötige Neukompilierung einer Quelle zu vermeiden, die sich nur um die Vorwärtsdeklaration kümmert, wenn sich eine Inline-Methodenimplementierung ändert.

Fazit

Sie sollten Ihre Codeorganisation sowohl so einfach wie möglich als auch so modular wie möglich gestalten. Fügen Sie so viel wie möglich in die Quelldatei ein. Stellen Sie nur in Headern bereit, was geteilt werden muss.

Aber an dem Tag, an dem Sie zirkuläre Abhängigkeiten zwischen Vorlagenobjekten haben, seien Sie nicht überrascht, wenn Ihre Codeorganisation etwas “interessanter” wird als die einfache Header- / Quellorganisation …

^_^

Zusätzlich zu allen anderen Antworten werde ich Ihnen sagen, was Sie NICHT in eine Header-Datei einfügen:
using Erklärung (das häufigste Wesen using namespace std;) sollten nicht in einer Header-Datei erscheinen, da sie den Namespace der Quelldatei, in der sie enthalten ist, verunreinigen.

  • +1 mit einer Einschränkung, die Sie verwenden können, solange es sich um einen detaillierten Namespace (oder einen anonymen Namespace) handelt. Aber ja, niemals verwenden using um Dinge in einem Header in den globalen Namensraum zu bringen.

    – KitsuneYMG

    22. Dezember 2009 um 12:40 Uhr

  • +1 Diese ist viel einfacher zu beantworten. 🙂 Auch Header-Dateien sollten nicht enthalten anonym Namensräume.

    – Sellibitze

    22. Dezember 2009 um 16:35 Uhr

  • Es ist in Ordnung, wenn Header-Dateien anonyme Namensräume enthalten, solange Sie verstehen, was das bedeutet, dh dass jede Übersetzungseinheit eine andere Kopie des von Ihnen definierten Namensraums haben wird. Inline-Funktionen in anonymen Namespaces werden in C++ für Fälle empfohlen, in denen Sie verwenden würden static inline in C99 wegen etwas, das damit zu tun hat, was passiert, wenn Sie interne Verknüpfungen mit Vorlagen kombinieren. Mit Anon-Namespaces können Sie Funktionen “verstecken”, während externe Verknüpfungen erhalten bleiben.

    – Steve Jessop

    22. Dezember 2009 um 19:04 Uhr

  • Steve, was du geschrieben hast, hat mich nicht überzeugt. Bitte wählen Sie ein konkretes Beispiel aus, bei dem Sie der Meinung sind, dass ein Anon-Namespace in einer Header-Datei absolut sinnvoll ist.

    – Sellibitze

    23. Dezember 2009 um 19:23 Uhr

Was kompiliert sich in nichts (binärer Fußabdruck von null) geht in die Header-Datei.

Variablen werden nicht zu nichts kompiliert, sondern Typdeklarationen (weil sie nur beschreiben, wie sich Variablen verhalten).

Funktionen tun dies nicht, aber Inline-Funktionen (oder Makros) tun dies, da sie nur dort Code erzeugen, wo sie aufgerufen werden.

Vorlagen sind kein Code, sie sind nur ein Rezept zum Erstellen von Code. also gehen sie auch in h-Dateien.

Im Allgemeinen fügen Sie Deklarationen in die Header-Datei und Definitionen in die Implementierungsdatei (.cpp) ein. Ausgenommen hiervon sind Templates, bei denen die Definition auch im Header stehen muss.

Diese und ähnliche Fragen wurden bei SO häufig gestellt – siehe Warum Header-Dateien und .cpp-Dateien in C++? und C++ Header Files, Code Separation zum Beispiel.

  • Natürlich können Sie auch Klasse setzen Definitionen in Header-Dateien. Es müssen nicht einmal Vorlagen sein.

    – Sellibitze

    22. Dezember 2009 um 16:24 Uhr

1646940012 868 Was sollte in eine h Datei kommen
Asch

Hauptsächlich Header-Datei enthalten Klasse Skelett oder Erklärung (ändert sich nicht oft)

und cpp-Datei enthält Klasse Umsetzung (ändert sich häufig).

  • Natürlich können Sie auch Klasse setzen Definitionen in Header-Dateien. Es müssen nicht einmal Vorlagen sein.

    – Sellibitze

    22. Dezember 2009 um 16:24 Uhr

1646940013 477 Was sollte in eine h Datei kommen
Shidouuu

Kopfzeile (.h)

  • Makros und Includes, die für die Schnittstellen benötigt werden (so wenige wie möglich)
  • Die Deklaration der Funktionen und Klassen
  • Dokumentation der Schnittstelle
  • Deklaration von Inline-Funktionen/-Methoden, falls vorhanden
  • extern zu globalen Variablen (falls vorhanden)

Körper (.cpp)

  • Restliche Makros und Includes
  • Fügen Sie den Header des Moduls ein
  • Definition von Funktionen und Methoden
  • Globale Variablen (falls vorhanden)

Als Faustregel gilt, dass Sie den „freigegebenen“ Teil des Moduls auf die .h-Datei (den Teil, den andere Module sehen können müssen) und den „nicht gemeinsam genutzten“ Teil auf die .cpp-Datei legen

PD: Ja, ich habe globale Variablen eingefügt. Ich habe sie einige Male verwendet und es ist wichtig, sie nicht in den Headern zu definieren, sonst erhalten Sie viele Module, von denen jedes seine eigene Variable definiert.

  • Als Faustregel gilt, dass so wenige Includes wie möglich in der .h-Datei enthalten sein sollten, und die .cpp-Datei sollte alle Header enthalten, die sie benötigt. Das verkürzt die Kompilierungszeiten und verschmutzt keine Namespaces.

    – David Thornley

    23. Dezember 2009 um 16:54 Uhr

988780cookie-checkWas sollte in eine .h-Datei kommen?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy