Trennung Header und Hauptteil



  • Hmm. Habe jetzt nicht alles mitverfolgt, aber mal ganz einfach:

    irgendwas.h

    #ifnedf IRGENDWAS_H //Headerguards
    #define IRGENDWAS_H
    
    class foo
    {
    public:
        void bar (); // Deklaration
    };
    
    #endif
    

    irgendwas.cpp

    #include "irgendwas.h" // Klassendefinition includen
    #include <iostream> //damit wir cout  benutzen können :yum: 
    
    void foo::bar () //Definition der in der Klasse deklarierten Funktion
    {
       std::cout << "irgendwas";
    }
    

    main.cpp

    #include "irgendwas.h" //Klassendefinition include, da wir diese Klasse benutzen wollen
    
    int main ()
    {
        foo f;
        f.bar ();
    }
    

    Was dich ev. verwirrt ist das ganze Deklaration/Definitions Zeugs. 😉
    Was das bei einer Funktion bedeutet hast du gesehen. Nun kann man das ganze auch für eine Klasse machen..

    irgendwas_anderes.h

    //.. Headerguards..
    
    class foo; // Deklaration von foo,weil die Implementierung nicht wichtig ist (hier), da es nur ein Zeiger ist..
    
    class ptr
    {
        foo* p;
    public:
        void machwas (); // wieder das gleiche, wie oben
    };
    

    irgendwas_anderes.cpp

    #include "irgendwas_anderes.h" // Wir müssen wissen, wie die Schnittstelle aussieht, damit wir die Funktionen implemenieren können.
    #include "irgendwas.h" // Müssen wir nun auch wissen, damit wir den ptr verwenden können.
    
    void ptr::machwas () //dasselbe
    {
        p->bar ();
    }
    

    Wenn du jetzt noch verwirrter bist, dann schlaf drüber und schau dir das nochmal in Ruhe an. 😉 Wenn du glaubst es verstandne zu haben, googlest du mal nach pImpl - Idiom und versuchst das zu verstehen. Wenn du das dann wirklich verstehst, dann hast du das ganze Spiel verstanden. 😉

    Noch eins:
    Der Name der Header und der cpp müssen nicht gleich sein, das ist nur so Konvention, weil alles andere keinen Sinn machen und verwirren würde. (eigl. logisch, dass die gleich heissen, nicht?)
    Wichtig ist aber nur, dass die Header includes stimmen (worauf dich aber dein Compiler sonst freundlich aufmerksam macht. ;))


  • Administrator

    Fast2 schrieb:

    Dravere schrieb:

    Vielleicht könnte man es mit ein paar g++ Befehle erklären. Dein präsentierter Code, würde man so kompilieren. Im ersten Schritt diesen Befehl:

    g++ -c Beispiel.cpp
    

    -c bedeutet nur kompilieren, keine ausführbare Datei zu erstellen. Dafür wird eine Beispiel.o erstellt. Eine sogenannte Objektdatei.

    Im zweiten Schritt setzen wir das nun zusammen:

    g++ -o Beispiel.exe AnderesBeispiel.cpp Beispiel.o
    

    -o bedeutet das man nun eine ausführbare Datei erstellt und benennt: Beispiel.exe. Man kompiliert AnderesBeispiel.cpp und fügt die Beispiel Objektdatei dazu. Der Linker wird dann die Definition in den Objektdateien (man kann auch mehrere benennen) suchen gehen, wenn er sie nicht in AnderesBeispiel.cpp findet.

    Grüssli

    OK, nach mehrmaligem Lesen hat sich hier grad ein Missverständnis meinerseits aufgelöst, nur wie bringe ich den Compiler/Linker dazu das selbe ohne den Zwischenschritt zu machen?

    Das geht nicht, der Zwischenschritt ist IMMER nötig! Und jede IDE macht diesen Zwischenschritt auch, nur bekommt man es vielleicht nicht mit, da es automatisiert von statten geht. Dies geschiet über sogenannte Makefiles, welche genau beschreiben, wie die einzelnen Schritte auszusehen haben.

    Wenn du nur mit einem Texteditor und zum Beispiel g++ zusammenarbeitest, musst du jeden einzelnen Zwischenschritt selber erledigen. Anders geht es gar nicht.

    Grüssli



  • Badestrand schrieb:

    Der Compiler schert sich nicht um Dateinamen.

    Einige Compiler reagieren allergisch auf unpassende Dateiendungen. Der gcc z.B. entscheidet nach der Dateiendung welches Compilerfrontend er benutzt (Ada, C, C++, Objective-C, Fortran, Pascal). Für C++ sind die folgenden Dateiendungen gebräuchlich ".C", ".cc", ".cxx", ".cpp".



  • @drakon: Ich verstehe nur nicht wie der Code von der irgendwas.cpp in die main.cpp kommt... du inkludierst ja nur die Headerdatei, in der aber kein Vermerk auf die irgendwas.cpp steht. 😞
    Edit: Oh, ich war schon beim Verfassen und habe dswegen nicht bemerkt, dass etwas neues geschrieben wurde.
    @Davere: Ja, JETZT verstehe ich das auch, man muss die Datei nicht im Code angeben, sondern nur über eine Objekt-Datei für den Linker sichtbar machen! 👍 🙂 🙄.
    Das war jetzt aber eine schwere Geburt! 🤡
    Und wir haben mal wieder (zum Teil) aneinander vorbeigeredet... :headbang:
    PS: Ich habe Code::Blocks und das schreibt die Makefiles selbst. 🙄



  • Fast2 schrieb:

    @drakon: Ich verstehe nur nicht wie der Code von der irgendwas.cpp in die main.cpp kommt... du inkludierst ja nur die Headerdatei, in der aber kein Vermerk auf die irgendwas.cpp steht. 😞

    Meinst du nicht, das wurde inzwischen genügend häufig in diesem Thread erklärt? 🙄

    Les diesen Thread am besten noch einmal durch. Ansonsten findest du im Internet sicher genügend Informationen zu der Vorgehensweise von Präprozessor, Compiler und Linker. Im Übrigen hab ich auch nicht das Gefühl, es würde etwas ändern, wenn ich den Vorgang erneut erläutern würde.



  • Ach ja, vergessen:
    Danke!



  • ~john schrieb:

    Badestrand schrieb:

    Der Compiler schert sich nicht um Dateinamen.

    Einige Compiler reagieren allergisch auf unpassende Dateiendungen. Der gcc z.B. entscheidet nach der Dateiendung welches Compilerfrontend er benutzt (Ada, C, C++, Objective-C, Fortran, Pascal). Für C++ sind die folgenden Dateiendungen gebräuchlich ".C", ".cc", ".cxx", ".cpp".

    Wenn man unbedingt will, sollte man das auch irgendwo in der IDE anpassen können. (VC geht das).

    @Fast2:
    Na also. Geht doch. 🙂


  • Administrator

    ~john schrieb:

    Badestrand schrieb:

    Der Compiler schert sich nicht um Dateinamen.

    Einige Compiler reagieren allergisch auf unpassende Dateiendungen. Der gcc z.B. entscheidet nach der Dateiendung welches Compilerfrontend er benutzt (Ada, C, C++, Objective-C, Fortran, Pascal). Für C++ sind die folgenden Dateiendungen gebräuchlich ".C", ".cc", ".cxx", ".cpp".

    Du könntest natürlich auch so klug sein und anstatt gcc g++ verwenden. Das ist also nicht ein Problem des Compilers, sondern des Frontends, also des Programmes gcc, welches aber nichts mit dem C++ Compiler zu tun hat. 😉

    Ich bin mir jetzt nicht mehr sicher und habe an dieser Maschine keinen gcc, aber ich dachte man könnte sogar explizit angeben, welchen Compiler man möchte, wenn man einfach nur den gcc verwendet.

    @Fast2,
    Wusste ich doch, dass die Compiler Befehle helfen würden. Gerngeschehen.

    Grüssli



  • Naja, die Compilerbefehle waren jetzt nicht allein das ausschlaggebende, eher der Hinweis, dass das automatisch passiert, da ich einfach nicht verstanden habe, wie jetzt der Code von DateiA nach DateiB kommt. Ich dachte da gibt es einen speziellen Befehl, oder Syntax, oder so. 😉
    Muss ich den Thread jetzt irgendwo, irgendwie auf gelöst stellen?
    PS: Irgendwie ist das Konzept (bei großen Projekten) doch doof: Der Linker muss sich ja durch die gesamte Objekt-Datei wühlen, nur um die Definition der Funktion zu finden, was ich mir bei großen Dateien schon als Bremse vorstellen könnte. Oder schreibt der Compiler eine Art "Inhaltsverzeichnis" (so hötte ich das gemacht), in dem dann irgendwie die Zeilennummern drinstehen, oder so? (Nur aus Interesse)


  • Administrator

    Fast2 schrieb:

    Naja, die Compilerbefehle waren jetzt nicht allein das ausschlaggebende, eher der Hinweis, dass das automatisch passiert, da ich einfach nicht verstanden habe, wie jetzt der Code von DateiA nach DateiB kommt. Ich dachte da gibt es einen speziellen Befehl, oder Syntax, oder so. 😉

    Naja, auf die Compilerbefehle kam deine Frage, wie man es sonst macht und dann hast du erfahren, dass es automatisch geht. Denke schon, dass dies ziemlich ausschlaggebend war. Aber ist ja auch egal 🙂

    Fast2 schrieb:

    Muss ich den Thread jetzt irgendwo, irgendwie auf gelöst stellen?

    Nein, das machen wir hier nicht, weil es sowieso nicht gemacht werden würde, wenn es sowas hätte. Der Thread wird sich langsam in den Untiefen des Archivs verlieren.
    *den Thread streichel* schon gut Threadilein, du wirst dort viele Freunde treffen 🤡

    Fast2 schrieb:

    PS: Irgendwie ist das Konzept (bei großen Projekten) doch doof: Der Linker muss sich ja durch die gesamte Objekt-Datei wühlen, nur um die Definition der Funktion zu finden, was ich mir bei großen Dateien schon als Bremse vorstellen könnte. Oder schreibt der Compiler eine Art "Inhaltsverzeichnis" (so hötte ich das gemacht), in dem dann irgendwie die Zeilennummern drinstehen, oder so? (Nur aus Interesse)

    Also vielleicht noch ein paar zusätzliche Bemerkungen dazu, bzw. noch ein Beispiel:
    Header.hpp

    void first();
    void second();
    void third();
    

    First.cpp

    #include "Header.hpp"
    
    void first()
    {
    }
    

    Second.cpp

    #include "Header.hpp"
    
    void second()
    {
      third();
    }
    

    Third.cpp

    #include "Header.hpp"
    
    void third()
    {
      first();
    }
    

    Main.cpp

    #include "Header.hpp"
    
    int main()
    {
      second();
      third();
    
      return 0;
    }
    

    g++ Befehle dazu:

    g++ -c First.cpp
    g++ -c Second.cpp
    g++ -c Third.cpp
    g++ -o Test.exe First.o Second.o Third.o
    

    Wie du nun unschwer erkennen kannst, muss man die Objektdateien erst ganz am Ende übergeben, wenn die ausführbare Datei erstellt wird. Die Objektdateien selber brauchen nichts über andere Objektdateien zu wissen. Der Linker setzt am Ende alles zusammen und erstellt die ausführbare Datei. Er ist quasi der letzte Schritt beim Erstellen eines Programmes.

    Ich kenne mich jetzt nicht genau in dem Bereich aus, aber du kannst dir sicher sein, dass die Linker sehr optimiert sind. Funktionsnamen dienen zur eindeutigen Identifizierung von den Funktionen. Man kann grundsätzlich über Bäume einfach die entsprechenden Funktionen finden. Häufig werden glaub ich auch Hashwerte für die Funktionen erstellt, wodurch man noch etwas mehr an Geschwindigkeit rausholen kann. usw. usf.

    Es hat aber definitiv seine Vorteile, wenn du erst am Ende linken musst. Du kannst so während der Entwicklung immer nur kleinere Bereich kompilieren und damit schauen, ob der C++ Code auch stimmt. Ohne jedesmal noch alle anderen Bereiche neu zu komplieren oder zu linken, welche der entsprechende Bereich benötigt.

    Grüssli



  • Um mal den Thread aus den Untiefen und von seinen Thread-Freunden weg zu holen.

    Eine Frage:
    Wäre es im Bezug auf große Projekte nicht einfach und sinnvoll, eine eigene "Bibliothek" oder Funktionssammlung zu schreiben? Also ich habe einen Header der alle Deklarationen vornimmt. Und eine z.B. my_lib.cpp in der die Funktionen komplett drinnen stehen. In meiner main.cpp würde ich dann einfach die Funktionen aufrufen und hab dort ein sehr einfachen/kurzen Code.

    Das ist eher mein Problem, dass ich mir nicht ganz sicher wäre was ich alles in eine extra *.cpp packe und was ich meiner "Hauptdatei" drinnen lasse. 😕

    AlphaX2



  • AlphaX2 schrieb:

    Um mal den Thread aus den Untiefen und von seinen Thread-Freunden weg zu holen.

    Ganz schlechte Idee. Erstelle einen neuen Thread und verlinke den alten, wenn du dich auf ihn beziehen willst.

    AlphaX2 schrieb:

    Also ich habe einen Header der alle Deklarationen vornimmt. Und eine z.B. my_lib.cpp in der die Funktionen komplett drinnen stehen.

    Das ist ja die Standardvorgehensweise, nur dass es oft mehrere .hpp- und .cpp-Dateien werden, je nach Funktionalität eben.


Anmelden zum Antworten