2 Klassen die sich gegenseitig einbinden.



  • Hallo zusammen,

    habe folgendes Problem, ich muss für eine Klausur ein Programm schreiben in dem 2 Klassen voneinander abhänig sind, sprich sich gegenseitig includen. Nach durchforsten dieses Forums hab ich rausgefunden das ich das mit einer vorwärtsdeklaration lösen kann. Hat auch funktioniert!!! Nun möchte ich in klasse A einen Container(list) haben der aus Zeigern auf Klasse B besteht. Klasse B soll eine Referenz auf Klasse A haben zu der sie gehört. Habe jetzt mal ein einfaches Programm geschrieben was ungefähr die Anforderungen aber sonst keinen grossen Sinn hat.

    Das Programm enthält die Klassen Fahrzeug und Teile, hier poste ich mal beide Header Dateien.

    //Fahrzeug.h
    //Hier soll als Attribut eine Liste aus Zeigern
    //auf die Klasse Teile rein
    #pragma once
    #ifndef FAHRZEUG_H
    #define FAHRZEUG_H

    #include<string>
    #include<list>
    #include"Teile.h"

    class Fahrzeug
    {
    public:
    Fahrzeug();
    Fahrzeug(const std::string&,const double&,const int&); // name,leerGewicht, raeder
    ~Fahrzeug();
    void beschleunigen(const double&);
    void addGewicht(const double&);
    void printData(void);

    //----- Set&Get Methoden -----

    void setGewicht(const double&);
    void setName(const std::string&);
    void setRaeder(const int&);
    std::string getName(void);

    private:
    std::string name;
    int raeder;
    double speed;
    double gewicht;
    double leerGewicht;
    Teile *teilePtr; //<-- das hier soll die liste geben "list<Teile> alleTeile"
    };

    #endif

    ****************************************************************************

    //Teile.h
    //Diese Klasse soll als Attribut eine Referenz auf Fahrzeug haben
    #pragma once
    #ifndef TEILE_H
    #define TEILE_H

    #include<string>

    class Fahrzeug; //Vorwärtsdeklaration

    class Teile
    {
    public:
    Teile();
    Teile(const std::string&, const double&,Fahrzeug&); //name, gewicht, fahrzeug zu dem Teil gehört
    ~Teile(void);
    void printData(void);

    //----- Set&Get Methoden -----

    void setName(const std::string&);
    void setGewicht(const double&);
    double getGewicht(void);
    std::string getName(void);

    private:
    std::string name;
    double gewicht;
    Fahrzeug *zuDiesemFahrzeugGehoehreIch;
    };

    #endif

    Wenn ich so kompilieren lasse läuft das Programm einwandfrei, wenn ich jetzt aber in Fahrzeug.h den zeiger durch eine liste(wie oben kommentiert) ersetze bekomme ich 9 Fehler, eigentlich nur 3 aber diese bei jedem kompilieren von main.cpp, Teile.cpp, und Fahrzeug.cpp <-- Vermutlich weil die Fahrzeug.cpp in Teile und main included wird?!?!

    ------ Erstellen gestartet: Projekt: Test, Konfiguration: Debug Win32 ------
    Kompilieren...
    Fahrzeug.cpp
    c:\dokumente und einstellungen\michael\eigene dateien\fh darmstadt\6. semester\pg ii\übung\test\test\fahrzeug.h(32) : error C2143: Syntaxfehler: Es fehlt ';' vor '<'
    c:\dokumente und einstellungen\michael\eigene dateien\fh darmstadt\6. semester\pg ii\übung\test\test\fahrzeug.h(32) : error C4430: Fehlender Typspezifizierer - int wird angenommen. Hinweis: "default-int" wird von C++ nicht unterstützt.
    c:\dokumente und einstellungen\michael\eigene dateien\fh darmstadt\6. semester\pg ii\übung\test\test\fahrzeug.h(32) : error C2238: Unerwartete(s) Token vor ';'

    Wäre nett wenn jemand die Zeit opfern könnte um sich das mal anzuschauen. Wäre für jeden Rat dankbar 😉

    MFG

    michl`



  • Habe den Post aus versehen nicht mit meinem Login geschrieben, also der Post oben ist von mir 🙂



  • michael_vhm schrieb:

    Wäre nett wenn jemand die Zeit opfern könnte um sich das mal anzuschauen. Wäre für jeden Rat dankbar 😉

    Wäre nett wenn du vorher die Zeit opfern kannst das ganze mit den cpp-Tags zu versehen und sauber zu formatieren. Hierdurch kämpft sich kein Mensch.



  • Ok, entschuldigung, aber was genau meinst du mit cpp Tags? Werde es dann heute abend editieren?!?!?! Und wie genau formatieren? Habe doch die .h dateien gepostet sind doch übersichtlich oder meinst du kommentare mit //standart Konstruktor usw.?



  • michl` schrieb:

    Ok, entschuldigung, aber was genau meinst du mit cpp Tags? Werde es dann heute abend editieren?!?!?! Und wie genau formatieren? Habe doch die .h dateien gepostet sind doch übersichtlich oder meinst du kommentare mit //stan**** Konstruktor usw.?

    Wenn du einen Eintrag postest, gibt es darunter etliche Icons und beschriftete Buttons. Da wählst du den "C/C++" Button 2*aus, Kopierst deinen Code zwischen diese beiden Tags und gehst mal noch weiter unten auf Vorschau.

    Dann formatierst du deinen Code lesbar (Sprich mit Einrückungen etc.).

    cu André



  • Da ich jetzt mal nett und freundlich bin:

    Das Ergebnis sollte in etwa so aussehen:

    //Fahrzeug.h
    //Hier soll als Attribut eine Liste aus Zeigern
    //auf die Klasse Teile rein
    #pragma once
    #ifndef FAHRZEUG_H
    #define FAHRZEUG_H
    
    #include<string>
    #include<list>
    #include"Teile.h"
    
    class Fahrzeug
    {
      public:
        Fahrzeug();
        Fahrzeug(const std::string&,const double&,const int&); // name,leerGewicht, raeder
        ~Fahrzeug();
        void beschleunigen(const double&);
        void addGewicht(const double&);
        void printData(void);
    
        //----- Set&Get Methoden -----
    
        void setGewicht(const double&);
        void setName(const std::string&);
        void setRaeder(const int&);
        std::string getName(void);
    
      private:
        std::string name;
        int raeder;
        double speed;
        double gewicht;
        double leerGewicht;
        Teile *teilePtr; //<-- das hier soll die liste geben "list<Teile> alleTeile"
    };
    
    #endif
    
    //Teile.h
    //Diese Klasse soll als Attribut eine Referenz auf Fahrzeug haben
    #pragma once
    #ifndef TEILE_H
    #define TEILE_H
    
    #include<string>
    
    class Fahrzeug; //Vorwärtsdeklaration
    
    class Teile
    {
      public:
        Teile();
        Teile(const std::string&, const double&,Fahrzeug&); //name, gewicht, fahrzeug zu dem Teil gehört
        ~Teile(void);
        void printData(void);
    
        //----- Set&Get Methoden -----
    
        void setName(const std::string&);
        void setGewicht(const double&);
        double getGewicht(void);
        std::string getName(void);
    
      private:
        std::string name;
        double gewicht;
        Fahrzeug *zuDiesemFahrzeugGehoehreIch;
    };
    
    #endif
    


  • Ok, danke für die antwort.
    Aber, da ich den Text ja leider als unreg. User geschrieben habe,
    weiss ich leider nicht wie ich diesen jetzt editieren kann.
    Könntest du mir da vielleicht noch bescheid geben? Oder soll ich ihn noch
    einmal Posten? Aber das wird bestimmt auch nicht gerne gesehen, oder?

    MFG

    michl`



  • Ok, vielen dank 🙂
    Das hat zumindest meine letzten paar Fragen beantwortet.

    MFG

    michl`



  • Und nun nehme ich mal deinen Code auseinander:

    1. Entweder #pragma once oder Includeguards, ich würde (da portabel) zu letzteren tendieren.

    2. Gewöhn dir bitte gleich ab im Header die Bezeichner bei den Parametern weg zu lassen.
    a) Man schaut um einen Überblick zu bekommen immer erst in den Header
    b) Wenn du Hilfe willst sollten wir den Code auch lesen können

    Also statt...

    Fahrzeug(
          const std::string&,
          const double&,
          const int&); // name,leerGewicht, raeder
    

    ...bitte:

    Fahrzeug(
          const std::string& name,
          const double& leerGewicht,
          const int& raeder);
    

    3. Const Referenzen sind schon gut, aber bitte nicht bei den einfachen Datentypen wie int (und ich würde es auch nicht für double machen), sondern in dem Fall ist call-by-value besser.

    Fahrzeug(
          const std::string& name,
          double leerGewicht,
          int raeder);
    

    4. Wie hast du versucht die Liste zu verwenden?
    Ich hoffe mal so:

    std::list<Teile> teile;
    

    5. Es ist kein Fehler, aber bei (void) wird üblicherweise eigentlich nur () geschrieben.

    6. Getter sollten in der Regel keine Zustände ändern, daher als const deklariert werden (Gilt möglichst für alle nicht-modifizierenden Methoden).

    std::string getName() const;
    

    7. Modifizierst du in dem folgenden Konstruktor das Fahrzeug?

    Teile(
          const std::string& name,
          double gewicht,
          Fahrzeug& fahrzeug);
    

    Wenn nicht bitte als const Referenz übergeben.

    8. Auch kein Fehler, aber vielleicht für die Zukunft leichter...

    Bei Konstanten gibt es mehrere mögliche Schreibweisen. Das const vor dem Bezeichner zu setzen ist zugegebener Maßen nicht unüblich, Dahinter hat aber aus Konsistenzgründen einen Vorteil:

    const int & a; // Ist identisch mit
        int const & a;
    
        const int * a; // Ist identisch mit
        int const * a; // aber nicht mit
        int * const a;
    
        // Wenn man sich das const hinter den Bezeichern angewöhnt, kann
        // man folgendes...
        int const * const a;
        // Als int-Konstante Zeiger-Konstante lesen
        // Damit sollte bei den folgenden beiden Fällen eindeutlich sein,
        // welcher Anteil tatsächlich konstant ist:
        int const * a; // int-Konstante Zeiger (Wert konstant)
        int * const a; // int Zeiger-Konstante (Zeiger konstant)
    

    cu André



  • Erstmal danke ich dir recht herzlich für deine Mühe 😉

    zu 1. Ok, dann streiche ich das

    #pragma once
    

    und nehme die include
    Guards.

    zu 2. Hab ich am anfang gemacht, aber dann in einem Buch gelesen das man das
    auch weg lassen kann, an die lesbarkeit habe ich garnicht gedacht 😮

    zu 4. NEIIIIN, habe als ich einen Thread über Vorwärtsdeklaration hier im
    Forum gelesen habe gelesen das mann

    using namespace std;
    

    nicht in die Header Datei schreiben soll, habe es rausgelöscht und
    alles geändert bis auf die Liste 👎
    Wenn ich eine Liste aus zeigern haben möchte mach ich das dann so:

    std::list<Teile*> alleTeile;
    

    ?

    zu 6. Werde gleich morgen früh noch alle Set/Get Methoden const
    machen(wird den Prof bestimmt auch milder stimmen)

    zu 7. Nein Modifiziere ich nicht, habe aber in diesem beispiel im Fahrzeug-
    Konstruktor gleich ein Teile-Objekt dynamisch erzeugt und mittels
    "this" das Fahrzeug an das Teile Objekt(über den Konstruktor) übergeben.

    Teile(
          const std::string& name,
          double gewicht,
          const Fahrzeug& fahrzeug);
    

    Als ich das const deklariert hatte habe ich mit dem "this"
    probleme bekommen(glaube zumindest das es das war, bin momentan auf
    arbeit und kann erst morgen früh wieder an den Code ran.) Werde es dann
    nochmal mit "const" versuchen!!

    Wie gesagt werde alle deine Verbesserung Vorschläge morgen umsetzen und dann berichten, falls noch was offen sein sollte, habe ich hoffentlich alle Gutmütigkeit nicht heute schon aufgebraucht 😉
    Vielen dank nochmal und einen schönen Abend!!

    MFG

    michl`



  • michl` schrieb:

    zu 2. Hab ich am anfang gemacht, aber dann in einem Buch gelesen das man das
    auch weg lassen kann, an die lesbarkeit habe ich garnicht gedacht 😮

    Nicht alles was man kann, macht auch Sinn 😉

    michl` schrieb:

    Wenn ich eine Liste aus zeigern haben möchte mach ich das dann so:

    std::list<Teile*> alleTeile;
    

    ?

    Ja. Dann sollte reicht zudem eine Vorwärtsdeklaration aus. ABER: du musst dann auch händisch den Speicher aufräumen (Die Liste macht kein automatisches delete).

    michl` schrieb:

    zu 6. Werde gleich morgen früh noch alle Set/Get Methoden const
    machen(wird den Prof bestimmt auch milder stimmen)

    Die Setter bitte nicht. Mit dem "const" hinter der Funktion zeigst du an, das diese Methode nichts am Objektzustand ändert. Die Setter ändern aber den Zustand, die Getter in der Regel nicht.

    michl` schrieb:

    zu 7. Nein Modifiziere ich nicht, habe aber in diesem beispiel im Fahrzeug-
    Konstruktor gleich ein Teile-Objekt dynamisch erzeugt und mittels
    "this" das Fahrzeug an das Teile Objekt(über den Konstruktor) übergeben.

    Dies sollte gehen, solange du auf nichts vom Fahrzeug zugreifst. Das this sollte auch kein Problem mit const bereiten, solange du nichts am Objekt änderst.

    Aber: Wenn du deine Getter nicht als const deklariert hast, gilt auch der Aufruf der Getter als Modifizierend (der Compiler prüft nicht nach ob die getter nichts ändert, andersherum meckert er aber wenn du etwas als const deklariertes änderst). Daher: Alle Methoden die keine Member ändern immer als const deklarieren [Siehe auch hier].

    cu André



  • Guten Tag,

    also habe jetzt nochmal getestet und es funktioniert soweit alles 👍
    Dafür danke ich nochmal.
    Jetzt habe ich aber noch ein kleines problem entdeckt.

    Meine Klasse Fahrzeug hat ja eine Liste aus Zeigern die auf Teile objekte zeigt.

    std::list<Teile*> alleTeile;
    

    jetzt will ich alle Teile objekte die ich dort eingefügt habe ausgeben, bekomme dort aber Fehlermeldungen

    void Fahrzeug::showTeile() const {
    	list<Teile*>::iterator pos;
    
    	for(pos = alleTeile.begin(); pos != alleTeile.end(); pos++) {
    		pos->printData();
    
    	}
    }
    
    error C2679: Binärer Operator '=': Es konnte kein Operator gefunden werden, der einen rechtsseitigen Operanden vom Typ 'std::list<_Ty>::_Const_iterator<_Secure_validation>'
    akzeptiert (oder keine geeignete Konvertierung möglich)
    
    error C2839: Ungültiger Rückgabetyp 'Teile **' für überladenen Operator
     '->'
    
    error C2039: 'printData': Ist kein Element von 'std::list<_Ty>::_Iterator<_Secure_validation>'
    

    Hättest du da auch noch eine Idee woran das liegen könnte?

    MFG

    michl`



  • void Fahrzeug::showTeile() const {
        list<Teile*>::const_iterator pos;
    
        for(pos = alleTeile.begin(); pos != alleTeile.end(); pos++) {
            (*pos)->printData();
    
        }
    }
    


  • und wenn wir schon bei der Schleife sind noch 2 Kleinigkeiten:
    a) ++i ist i++ vorzuziehen, solange es keine Gründe dagegen gibt.
    Wenn man sich nicht auf die Optimierung des Compilers verlassen will bedeutet ++i ein temporäres Objekt weniger als i++.
    b) Bedenke welche Ausdrücke mehrfach ausgeführt werden, und versuche unnötige Aufrufe zu vermeigen:

    for(<Einmal zum Start>;<Jedesmal zum Schleifenbegin>;<Jedesmal zum Schleifenende>)

    // Bei mir sähe der Code so aus:
        void Fahrzeug::showTeile() const
        {
            for(list<Teile*>::const_iterator pos = alleTeile.begin(),
              end = alleTeile.end(); pos != end; ++pos)
                (*pos)->printData();
        }
    }
    

    Alternativ kann man die Deklaration der beiden Iteratoren pos und end natürlich auch aus dem Schleifenkopf ziehen.


  • Mod

    Auch ein Standardalgorithmus kommt in Betracht

    void Fahrzeug::showTeile() const
        {
            std::for_each( alleTeile.begin(), alleTeile.end(), std::mem_fun( &Teile::printData ) );
        }
    }
    


  • Ich danke allen recht herzlich für die vielen antworten. Hatte leider die ganze zeit kein Inet, deswegen hat es etwas länger gedauert.

    Hab diese Variante genommen:

    // Bei mir sähe der Code so aus:
        void Fahrzeug::showTeile() const
        {
            for(list<Teile*>::const_iterator pos = alleTeile.begin(),
              end = alleTeile.end(); pos != end; ++pos)
                (*pos)->printData();
        }
    }
    

    Funktioniert alles 👍

    Könnte mir vielleicht nur noch jemand erklären warum man bei

    (*pos)->printData();
    

    die Klammern um das *pos macht?

    MFG

    michl`



  • Das ist wegen der Rangfolge der Operatoren. -> hat eine höhere Priorität als * weswegen es zuerst ausgwertet würde. Durch die Klammerung veränderst du das.


Anmelden zum Antworten