Frage zu C++ Header/CPP Dateien



  • Hallo zusammen,
    ich habe ein paar Fragen zu den Header Dateien in C++.
    Hoffe ihr könnt mir die beantworten.

    1. Wofür braucht man die beiden Dateiformate?
    2. Was kommt in eine .cpp was in eine .h Datei?
    3. Wie viele Klassen, Enums etc. sollten sich durchschnittlich in einer .h / .cpp
      Datei befinden?


    1. Wofür braucht man die beiden Dateiformate?

    Für das Grundlegende Konzept der Übersetzungseinheiten, die durch eine Source-Datei (.cpp) repräsentiert werden.

    Die ODR, One Definition Rule, besagt:

    No translation unit shall contain more than one definition of any variable, function, class type, enumeration
    type, or template.

    Every program shall contain exactly one definition of every non-inline function or variable that is odr-used
    in that program [...]. The definition can appear explicitly in the program, it can be found
    in the standard or a user-defined library [...]

    Damit kommen wir auch schon zu 2.:

    1. Was kommt in eine .cpp was in eine .h Datei?

    Faustregel für Variablen und Funktionen: In die Source-Datei kommen die Definitionen, in die Header-Datei die Deklarationen.

    Die Idee ist, dass in einer Headerdatei alles deklariert wird - wenn du die Headerdatei dann einbindest, dann kannst du bspw. Funktionen nutzen, die dort deklariert wurden.
    Und zu jeder Headerdatei mit Deklarationen findest du eine Source-Datei mit Definitionen der entsprechenden Funktionen und Variablen. Meistens.

    Solange der Linker die Definition dann findet (ob nun in einer Übersetzungseinheit, oder eine Bibliothek die aus Übersetzungseinheiten erstellt wurde, usw.), ist alles gut. Sobald der die Definition irgendwo zweimal findet, oder gar nicht, hast du ein Problem.

    Mit dem Header wird nichts direkt gemacht. Der Compiler arbeitet nur mit den Source-Dateien (*.cpp).
    Allerdings kannst du über die Präprozessor-Direktive include Header einbinden - dabei werden die einfach Zeichen für Zeichen an die Stelle der include-Direktive kopiert.
    Beispiel:

    #include <iostream>
    
    int main()
    { 
        std::cout << "Hello TheSanix!\n";
    }
    

    iostream ist ein Header ohne Endung. Sein Inhalt wird vom Präprozessor dahin kopiert, wo die include-Direktive steht.
    In iostream wiederum stehen weiter include-Direktiven. Die werden auch durch den Inhalt der entsprechenden Header ersetzt.
    cout ist eine globale Variable, die in <iostream> deklariert wird. Würde sie dort definiert werden, fände der Linker später
    Mehrere Definitionen in verschiedenen Übersetzungseinheiten. Überall da, wo <iostream> eingebunden wurde.

    Bindest du also in verschiedenen Übersetzungseinheiten <iostream> ein, und linkst diese zu einem ausführbaren Programm.,
    ist alles in Ordnung - Deklarationen kann es soviele geben wie du willst. Würde da aber eine Definition stehen, wüsste der
    Linker nicht welche er nehmen sollte .

    1. Wie viele Klassen, Enums etc. sollten sich durchschnittlich in einer .h / .cpp
      Datei befinden?

    Ach, das ist unterschiedlich. Natürlich sollten die nie zu groß werden.

    Mehr dazu findest du hier: http://de.wikipedia.org/wiki/Header-Datei

    Einige weitere Bemerkungen:

    • Dadurch, dass die Definitionen für verschiedene Sachen in verschiedene Übersetzungseinheiten kommen, wird die Kompilierung erschnellt.
      Denn solange alle Übersetzungseinheiten kompiliert sind, müssen sie nur gelinkt werden, um das komplette Programm zu erstellen. Wenn nur eine Übersetzungseinheit neu kompiliert und dann mit den anderen gelinkt werden muss, geht das Verhältnismäßig schnell; schneller als wenn das komplette Projekt neu kompiliert werden muss weil alle Definitionen in einem Topf liegen.
    • Es gibt verschiedene konventionelle Endungen für Source- und Header-Dateien:
      Source: .cpp, .cxx, .cc, ..
      Header: .hpp, .h, .hxx, .hh, ...

    Jedoch sollte man innerhalb eines Projektes konsequent nur jeweils eins nutzen. Am bekanntesten sind .cpp und .h .

    • Es gibt den sog. function-specifier inline, der es ermöglicht, eine Funktionsdefinition in einem Header vorzunehmen.
      Tut man das, büßt man jedoch die Kompilierungsgeschwindigkeit ein, die im ersten Punkt erwähnt wurde - es muss jede Übersetzungseinheit die diesen Header einbindet neu kompiliert werden, sobald eine Definition o.ä. verändert wurde (!).

    Merke dir aber, dass das Alles hier eigentlich eher unnütz ist. Hole dir lieber ein gutes Grundlagenbuch, wenn du die Zusammenhänge schön verstehen möchtest - das hier ist auf die Schnelle zusammengeschustert.



  • TheSanix schrieb:

    1. Wofür braucht man die beiden Dateiformate?

    Dem Compiler ist das echt egal, wie du deine Dateien bennenst. Ist ist einfach nur Text mit C++-Quellcode drin, wobei die #include-Direktive wirklich nur eine Text-Ersetzung ist. Die Verwendung von Header-Dateien ist Konvention. Sie beinhalten das, was du in anderen "Modulen" inkludieren sollst, damit du das zur Header-Datei gehörige Modul verwenden kannst. Stichwort: Getrennte Übersetzung.

    TheSanix schrieb:

    1. Was kommt in eine .cpp was in eine .h Datei?

    eine cpp Datei ist das, was kompiliert wird, wobei der Präprozessor natürlich vorher all die ganzen Textbausteine aus anderen Dateien, die per #include referenziert werden, einfügt. Eine Header-Datei enthält typischerweise Funktionsdeklarationen, Funktionstemplatedefinitionen, Klassendeklarationen, Klassendefinitionen, Klassentemplates. Das ergibt sich aus der One-Definition-Rule. Die solltest du kennen und verstanden haben.



  • Vielen Dank für die Hilfe, hat mir sehr geholfen!
    C++ an sich kann ich recht gut, nur das mit den Headerdateien war mir nie so richtig klar.



  • TheSanix schrieb:

    Vielen Dank für die Hilfe, hat mir sehr geholfen!
    C++ an sich kann ich recht gut, nur das mit den Headerdateien war mir nie so richtig klar.

    Das widerspricht sich. Total.



  • Sone schrieb:

    Es gibt den sog. function-specifier inline, der es ermöglicht, eine Funktionsdefinition in einem Header vorzunehmen.

    Die Argumentation gefällt mir irgendwie nicht. Bzw. da hast du eine Behauptung ohne Begründung aufgestellt. Das gehört sich net, lernt man doch in Unterricht. :p



  • out schrieb:

    Sone schrieb:

    Es gibt den sog. function-specifier inline, der es ermöglicht, eine Funktionsdefinition in einem Header vorzunehmen.

    Die Argumentation gefällt mir irgendwie nicht. Bzw. da hast du eine Behauptung ohne Begründung aufgestellt. Das gehört sich net, lernt man doch in Unterricht. :p

    Wieso? Meinst du, der Grund warum es überhaupt inline gibt? 🙂 😃

    Der eigentliche Grund war früher, dass man dem Compiler einen Hinweis geben konnte, die Funktion zu inlinen. Heutzutage wird das aber einfach ignoriert - der Compiler macht es selbst, und entscheidet natürlich besser.

    Es gibt halt aber Situationen wo man nicht eine separate Source-Datei für die Definition erstellen möchte (bspw. Header-Only Bibliotheken o.ä.).
    Da bietet sich inline an.



  • Sone schrieb:

    out schrieb:

    Sone schrieb:

    Es gibt den sog. function-specifier inline, der es ermöglicht, eine Funktionsdefinition in einem Header vorzunehmen.

    Die Argumentation gefällt mir irgendwie nicht. Bzw. da hast du eine Behauptung ohne Begründung aufgestellt. Das gehört sich net, lernt man doch in Unterricht. :p

    Wieso? Meinst du, der Grund warum es überhaupt inline gibt? 🙂 😃

    Der eigentliche Grund war früher, dass man dem Compiler einen Hinweis geben konnte, die Funktion zu inlinen. Heutzutage wird das aber einfach ignoriert - der Compiler macht es selbst, und entscheidet natürlich besser.

    Es gibt halt aber Situationen wo man nicht eine separate Source-Datei für die Definition erstellen möchte (bspw. Header-Only Bibliotheken o.ä.).
    Da bietet sich inline an.

    Hab jetzt gedacht, du erwähnst jetzt noch sowas, wie dass man Funktionen schon innerhalb einer Header definieren kann. Der Compiler ist da glücklich, der Linker nicht, da es die ODR verletzt. Ergo inline = Funktion ODR-konform machen


Anmelden zum Antworten