Anfängerfrage: Statische Methode auslagern



  • Hallo allerseits,

    ich habe eine für die erfahrenen vermutlich sehr einfache Frage...

    Ich habe drei Dateien main.cpp, Car.h und Car.cpp, diese sehen wie folgt aus

    main.cpp

    #include "sources/Car.h"
    //#include "sources/Car.cpp" // einkommentiert funktioniert es
    
    int main(void)
    {
        Car::Show();
        return 0;
    
    }
    

    Car.h

    #include <iostream>
    using namespace std;
    
    class Car
    {
    public:
        static void Show();
    };
    

    Car.cpp

    #include "Car.h"
    
        void Car::Show()
        {
            int Year  = 2004;
            int Mileage = 24012;
            string Make  = "Ford";
            string Model = "Crown Victoria";
            unsigned NumberOfDoors = 4;
            cout << "\nYear:    " << Year;
            cout << "\nMileage: " << Mileage;
            cout << "\nMake:    " << Make;
            cout << "\nModel:   " << Model;
            cout << "\nDoors:   " << NumberOfDoors;
        }
    

    Frage: Warum funktionert das Programm wenn ich die Datei Car.cpp inkludiere, falls ich es aber nicht tue gibt "undefined reference" error? Die Funktion Show sollte statisch verfügbar sein, aber zur Klasse Car gehören.

    Ich versuche schon seit ein paar Stunden dieses Problem zu verstehen, komme aber nicht dahinter 😕

    Beste Grüße!



  • Du musst car.cpp compilieren und mit dem Compilat der main.cpp linken.



  • Hey, danke schonmal!

    Bin nicht ganz sicher ob ich das richtig verstehe. Muss ich Car.cpp extra compilieren nur weil dort auf einmal eine statische Methode drin ist? Ohne statische Methoden funkioniert es ja.

    Ich verwende Ubuntu, Eclipse, g++ (und Eclipse hat bisher immer ganz gut selbst entschieden wie kompiliert und gelinkt wurde)

    Danke!



  • Dieser Thread wurde von Moderator/in pumuckl aus dem Forum C++ (auch C++0x) in das Forum Compiler- und IDE-Forum verschoben.

    Im Zweifelsfall bitte auch folgende Hinweise beachten:
    C/C++ Forum :: FAQ - Sonstiges :: Wohin mit meiner Frage?

    Dieses Posting wurde automatisch erzeugt.



  • Petri schrieb:

    Frage: Warum funktionert das Programm wenn ich die Datei Car.cpp inkludiere, falls ich es aber nicht tue gibt "undefined reference" error?!

    Bei "undefined reference" handelt es sich um einen Fehler des Linkers. Wenn du den verstehen willst, musst du etwas wissen über den Kompilierungsvorgang, der in verschiedenen Schritten abläuft. Ich versuch diesen Mal mit einfachen Worten zu umschreiben, für genaueres solltest du mal ne Runde nach Stichworten wie "Compiler", "Assembler" und "Linker" googeln:

    Wenn du deine Datei kompilierst wird daraus vom Compiler deiner Wahl zuerst eine Objektdatei aus all dem Code erstellt, der enthalten ist. Die Objektdatei ist zu diesem Zeitpunkt aber noch nicht ausführbar, dies ist erst eine Vorstufe zum fertigen, ausführbaren Programm in Maschinencode!
    Unter Linux hat eine Objektdatei typischerweise die Endung ".o".

    Wenn du jetzt in deinem Code ein #include verwendest, wird an diese Stelle ganz einfach der Code kopiert, der in der betreffenden Datei drinsteht.

    Mit #include "sources/Car.h" wird dort also zuerst der ganze Inhalt des dort includeden Files <iostream> eingefügt und dann deine Definition der Klasse "Car". Für deinen weiteren Code (nur in diesem File wos included ist!!) bedeutet dies, dass der Compiler jetzt weis, wie die Klasse Car aufgebaut ist und du somit in deinem Code auf Methoden und Member der Klasse zugreifen kannst ohne Fehler zu erhalten. Das ist der Unterschied zu einer sogenannten "forward declaration", wo du nur "class Car;" hinschreibst und der Compiler dann zwar weis, dass es die Klasse gibt, er aber noch keine Member kennt und du somit auch nicht auf diese zugreifen kannst. Der wichtigste Punkt hier aber ist: Der Compiler nimmt nur File für File, er kann nicht selbständig über "Filegrenzen" hinwegschauen, alles externe muss von dir explizit eingebunden (über include) oder als solches gekennzeichnet werden.

    Wenn du diesen Code jetzt so kompilierst (zur Objektdatei), wird der Compiler feststellen, dass es zwar eine Klasse "Car" mit der statischen Methode Show() gibt und dir da noch keinen Fehler ausgeben, er wird aber auch feststellen, dass der Code dieser Methode nicht im File enthalten ist (das funktioniert genau gleich, egal ob die Methode statisch, nicht statisch, virtuell oder was auch immer ist). Dies ist für den Assembler (erster Teil des Compilers) noch in Ordnung, der erstellt dann einfach etwas, was du dir als eine Art "Platzhalter" (reference!!!) vorstellen kannst: Er markiert die Stelle und registriert diese auch in der sogenannten "Linking Table" und sagt so: Hey, hier fehlt noch was, da muss noch ein Link (Referenz) auf die richtige Funktion rein.
    Auf diese Weise werden im normalen Ablauf durch den Compiler alle Files durch den Assembler(üblicherweise Teil des Compilers 😉 ) assembliert.

    Der nächste Schritt ist dann, diese Objectfiles miteinander zu verlinken. Dies geschieht - wer hätte das gedacht - durch den "Linker". Hier werden alle verschiedenen Objectfiles zu einem vereint und der Linker sucht jetzt für all die obig erstellten Platzhalter in den anderen Files nach dem wirklichen Code der Funktionen und ersetzt die Platzhalter mit den Links zu den richtigen Funktionen. Erst wenn dies alles passiert ist, ist der Kompilierungsvorgang zu Ende und du kannst das so entstandene und jetzt hoffentlich funktionstüchtige Programm ausführen.

    Was bei deinem Fehler jetzt passiert ist, ist ganz einfach, dass der Linker, da er ja nur das File mit der Definition hatte und du daraus direkt ein funktionstüchtiges Programm machen wolltest, den Code der Funktion "Show()" nirgends nicht finden konnte. Die Referenz darauf im Code der Datei "main.cpp" bleibt damit also undefiniert und da das Programm so nicht ausführbar ist, gibt dir der Compiler den Fehler "undefined reference" zurück... 😉

    Im Fall wo du den Code includest hingegen, ist ja der Code derselben dann in der Datei "main.cpp" direkt selber enthalten und der Compiler kann den Aufruf derselbigen dann auch problemlos verlinken und direkt ein funktionstüchtiges Programm erstellen.. Klar soweit? 🕶

    Falls du dich jetzt fragst, ob du denn immer auch all die ".cpp"-Codefiles includen musst damits funktioniert, lautet die Antwort: Um Himmels willen nein!
    Du musst nur den Compiler richtig bedienen.

    Soll heissen zuerst nur die Objectfiles erstellen, also einfach mal kompilieren ohne zu Linken, in deinem Fall z.B. in der Konsole mit:

    g++ -c main.cpp ->Erstellt aus main.cpp die Objektdatei main.o ohne zu Linken
    g++ -c sources/Car.cpp ->dasselbe für den Code in Car

    Und um jetzt das ausführbare Programm zu erhalten, musst du die Files noch miteinander verlinken, was du in g++ machst mit:
    g++ main.o Car.o -o meinprogramm

    Die Direktive "-c" weist g++ an, nicht zu Linken und nur das Objectfile zu erstellen, mit der Direktive "-o" kannst du den Namen und auch den Pfad des Outputfils angeben. (So könntest du dann beispielsweise alle Objectfiles in einen eigenen Ordner pappen, damit du kein Chaos bekommst).

    Übrigens ist schon für kleinere Projekte die Verwendung eines sogenannten Makefiles sehr hilfreich, in dem du definieren kannst, wie dein Projekt zu kompilieren ist (du hast da die Möglichkeit, dem Computer zu sagen, in welcher Reihenfolge er welche Befehle ausführen soll, eine Art Shellscript). Dann musst du in der Konsole nur noch "make" eintippen und all diese obigen, mühsamen Schritte werden automatisch ausgeführt 😉

    Ich denk, das sollte die Frage geklärt haben 😃

    gruss

    Btw. Nur ne kurze Bemerkung zu deinem Code: benütze die Direktive "using namespace std;" nie in einem header file, da kannst du ganz schöne Probleme bekommen. Für ne Begründung google mal ein bisschen oder bemüh die Suchfunktion des Forums.



  • ja krass, vielen vielen Dank!!

    Als Fazit für alle anderen die evtl hierrüber stolpern: Es lag wohl wirklich daran wie Eclipse die Dateien durch den Compiler jagt. Mit der Erklärung&Anleitung von oben per Hand kompiliert funktioniert es einwandfrei!

    Seltsam ist wirklich nur warum es Eclipse dann manchmal richtig macht, und manchmal nicht (ich dachte wirklich es liegt an der statischen Methode). Dann werde ich jetzt wohl schauen wie ich mir ein Makefile zusammenbaue und Eclipse beibringe dieses zu verwenden...

    Besten Dank noch einmal!



  • Petri schrieb:

    Seltsam ist wirklich nur warum es Eclipse dann manchmal richtig macht, und manchmal nicht (ich dachte wirklich es liegt an der statischen Methode). Dann werde ich jetzt wohl schauen wie ich mir ein Makefile zusammenbaue und Eclipse beibringe dieses zu verwenden...

    Mit Eclipse hab' ich jetzt schon ne Weile nicht mehr gearbeitet, aber ich meine mich zu erinnern, dass du in den Optionen zum kompilieren irgendwo angeben kannst, welche Files alle kompiliert werden sollen. Wenn du nur mit Eclipse und ohne die Konsole arbeiten möchtest, solltest du dich da mal umschauen. Eclipse kann dies alles nämlich ganz bestimmt!

    Gruss


Anmelden zum Antworten