Pufferüberlauf: Finde Fehler nicht



  • Moin @all

    ich habe hier folgendes Programm. Zu Vollständigkeit poste ich mal alle Dateien.
    Nach meiner Auffassung befindet sich der Fehler in der Datei Message.cpp in Zeile 20. Wenn man diese Zeile auskommentiert gibt das Programm nicht irgendwas aus dem Puffer aus.
    Hier die Header:
    Folder.h

    #ifndef FOLDER_H
    #define FOLDER_H
    #include <set>
    #include "Message.h"
    using namespace std;
    
    class Message;
    
    class Folder
    {
        friend class Message;
    
    private:
        set<Message*> messages;
        void addMsg(Message *pmessage);
        void remMsg(Message *pmessage);
    };
    #endif // FOLDER_H
    

    Message.h

    #ifndef MESSAGE_H
    #define MESSAGE_H
    #include <set>
    #include <string>
    #include "Folder.h"
    
    using namespace std;
    
    class Folder;
    
    class Message
    {
        friend class Folder;
    public:
        Message(const string &str = ""): contents(str) {}
        Message(const Message&); //copy constructor
        Message& operator=(const Message&); //allocation constructor
        ~Message(); //destructor
        void save(Folder &fld); //add a message to a folder
        void remove(Folder &fld); //removes a message from a folder
    private:
        string contents;
    
        set<Folder*> folders; //set of Folders* where the Message object is saved
        void Put_Msg_in_Folders(const set<Folder*> &); // Put all Messages in Folders which belongs to the message
        void remove_Msg_from_Folders();
    
        void addFldr(Folder *pfolder);
        void remFldr(Folder *pfolder);
        set<Message*>::const_iterator iter;
    
    };
    
    #endif // MESSAGE_H
    

    Hier die Source Files:
    Message.cpp

    #include "Message.h"
    
    void Message::save(Folder &fld)
    {
    
        fld.addMsg(this);
        return;
    }
    
    void Message::remove(Folder &fld)
    {
        fld.remMsg(this);
        return;
    }
    
    void Message::addFldr(Folder *pfolder)
    {
        folders.insert(pfolder); //here must be an error!
        return;
    }
    
    void Message::remFldr(Folder *pfolder)
    {
    
        folders.erase(pfolder);
        return;
    }
    
    void Message::Put_Msg_in_Folders(const set<Folder*> &rhs)
    {
        for(set<Folder*>::const_iterator beg = rhs.begin(); beg != rhs.end(); ++beg)
            (*beg)->addMsg(this);
    }
    
    void Message::remove_Msg_from_Folders()
    {
        for(set<Folder*>::const_iterator beg = folders.begin(); beg != folders.end(); ++beg)
            (*beg)->remMsg(this);
    }
    
    Message::~Message()
    {
        remove_Msg_from_Folders();
    }
    
    Message::Message(const Message &m):
        contents(m.contents), folders(m.folders)
    {
        Put_Msg_in_Folders(folders);
    }
    
    Message& Message::operator=(const Message &rhs)
    {
        if(&rhs != this)
        {
            remove_Msg_from_Folders();
            contents = rhs.contents;
            folders = rhs.folders;
            Put_Msg_in_Folders(rhs.folders);
        }
        return *this;
    }
    

    Folder.cpp

    #include "Folder.h"
    
    void Folder::remMsg(Message *pmessage)
    {
        messages.erase(pmessage);
        pmessage->remFldr(this);
        return;
    }
    
    void Folder::addMsg(Message *pmessage)
    {
    
        messages.insert(pmessage);
        pmessage->addFldr(this);
        return;
    }
    

    main.cpp

    #include <iostream>
    #include "Folder.cpp"
    #include "Message.cpp"
    using namespace std;
    
    int main()
    {
        Message myMessage("Hallo");
        Folder myFolder;
        myMessage.save(myFolder);
        //Message my2Message(myMessage);
        //my2Message.remove(myFolder);
        //Message _2 = my2Message;
        return 0;
    }
    

    Sorry für so viel Code 😃
    Oben steht ja der Hinweis von mir.
    Der Compiler meldet übrigens keine Warnungen oder Fehler.



  • int main()
    {
        Message myMessage("Hallo");
        Folder myFolder;
        myMessage.save(myFolder);
    }
    

    Also verlässt myFolder den Scope von main als erstes.
    Kein Folder mehr...
    Was schlecht ist, da myMessage immer noch einen pointer auf myFolder in Message::folders hat.
    Und diesen auch benutzt!


  • Mod

    Das ist ja das reinste PingPong-Spiel zwischen Message und Folder 🙂 . Den Fehler hat Furble Wurble schon genannt. Da du aber ausserdem noch ein paar weitere dicke Hauer (nicht nur bei dem Design der Klassen, das zu diesem Fehler fuehrt) drin hast, hier eine kurze Liste:

    -cpp Dateien included man nicht. Guck dir nochmal an, wie das mit den Headern gedacht ist. So ist es falsch.
    -In Headern benutzt man nie ein globales using namespace! Der ganze Sinn von Namespaces ist sonst dahin.
    -Du hast eine zirkulare Abhaengigkeit zwischen Folder.h und Message.h. Das darf auch nicht sein! Soweit ich das sehe, brauchen beide Header den jeweils anderen ueberhaupt nicht und die bereits vorhandene forward-Deklaration der jeweils anderen Klasse reicht vollkommen aus.

    Das sind drei wirklich dicke Fehler, die musst du sowohl unbedingt korrigieren, als auch verstehen, warum man das so nicht macht. Alle drei sollten in jedem halbwegs brauchbaren Lehrbuch erklaert sein.

    Das mir das Design der Klassen nicht geheuer ist, muss ich wohl kaum sagen. Der Fehler kommt nicht aus dem Nichts, er ist eine unmittelbare Folge.



  • so problem fixed! Danke für eure Hinweise! Die Implementierung des Desktruktors hab ich bei der Folder Klasse völlig vergessen. Das das ein hin und her ist weiß ich. Ich habe es am Anfang auch nicht ganz verstanden warum man das so machen sollte. So wollte es mein Buch aber(C++ Primer 4th edition Auf Seite 575)
    Zu den namespace: ok using namespace std; werde ich nicht mehr machen.
    zu den includes: ok werde ich nicht mehr machen. Ich dachte frühere nicht das ich auch die source Datei automatisch mit einbinde wenn ich nur den Header includiere.

    Hinzugefügt:
    Ich habe eben aus

    #include "Folder.cpp"
    #include "Message.cpp"
    

    in main.cpp

    #include "Folder.h"
    #include "Message.h"
    

    gemacht. Jetzt kann ich es aber nicht mehr compilieren. Der Compiler sagt dann das er z.B. die Konstruktoren (die ja z.B. in Message.cpp definiert sind) nicht definiert auffinden kann. Ich weiß nicht warum. Ich habe eben ein kleineres Projekten zum testen erstellt in dem das allerdings funktioniert. In diesem Projekt zum testen bindet die main.cpp eine weapon.hpp ein. die weapon.cpp bindet die weapon.hpp ein. Wie kann das gehen??? Wie wird die weapon.cpp eingebunden?



  • ok. Nachdem ich bei meinem "Test Projekt" auch mehr als einen Header mit dazugehöriger Quelldatei gibst auch Probleme. Die gleichen wie bei dem Projekt mit den Message und Folder Objekten.


  • Mod

    Keksman schrieb:

    Zu den namespace: ok using namespace std; werde ich nicht mehr machen.

    Darfst du schon noch benutzen. Aber bitte nicht in Headern.

    zu den includes: ok werde ich nicht mehr machen. Ich dachte frühere nicht das ich auch die source Datei automatisch mit einbinde wenn ich nur den Header includiere.

    Tust du auch nicht. Siehe unten.

    Hinzugefügt:
    Ich habe eben aus

    #include "Folder.cpp"
    #include "Message.cpp"
    

    in main.cpp

    #include "Folder.h"
    #include "Message.h"
    

    gemacht. Jetzt kann ich es aber nicht mehr compilieren. Der Compiler sagt dann das er z.B. die Konstruktoren (die ja z.B. in Message.cpp definiert sind) nicht definiert auffinden kann. Ich weiß nicht warum. Ich habe eben ein kleineres Projekten zum testen erstellt in dem das allerdings funktioniert. In diesem Projekt zum testen bindet die main.cpp eine weapon.hpp ein. die weapon.cpp bindet die weapon.hpp ein. Wie kann das gehen??? Wie wird die weapon.cpp eingebunden?

    Der Uebersetzungsvorgang laeuft bei praktisch allen C++-Implementierungen wie folgt:
    Erst einmal wird jede Sourcedatei (deine cpp-Dateien) vom Compiler einzeln uebersetzt. Im Details wird dabei zuerst einmal der Praeprozessor ueber den Code gejagt, der, unter anderem, die ganzen includes aufloest. Inlcludes sind einfache Textersetzungen, der Inhalt der genannten Datei wird an der fraglichen Stelle eingefuegt. Erst dann kommt der Compiler dran, der den Code analysiert und auf Fehler prueft und letztendlich (in der Regel ueber ein paar Umwege zur Optimierung) Maschinencode erzeugt. Man erhaelt fuer jede Quellcodedatei eine Datei mit dem Maschinencode zu den Funktionen aus der Quelldatei, das nennt man "object code".
    Mit lauter einzelnen Objektdateien kann man noch nichts anfangen, die muessen zu etwas Ausfuehrbaren zusammen gesetzt werden. Das macht der Linker. Dem gibt man die ganzen Objektdateien und der guckt da drin nach der main-Funktion und nach den Funktionen, die von der main aufgerufen werden und so weiter und setzt alles zu einem ausfuehrbarem Programm zusammen*.
    Meistens bekommst du davon nichts mit, weil alles automatisch in einem Rutsch ablaueft (da die Leute, die die Implementierung geschrieben haben schon wussten, dass man diese Schritte nicht jedes Mal alle einzeln machen moechte). Wenn du aber mehrere Quelldateien hast, muss du langsam anfangen, zumindest ein bisschen selber mit Hand anzulegen, damit alles ineinander passt. Viele IDEs helfen dir dabei. Wenn du eine IDE benutzt, dann gibt es da sicherlich die Moeglichkeit, mehrere Quelldateien zu einem Projekt zusammen zu fassen. Dann wird der oben beschriebene Vorgang automatisch fuer alle diese Dateien zusammen durchgefuehrt. Wenn du von Hand compilierst, versuch mal, beim Aufruf des Compilers einfach alle Quelldateien gleichzeitig anzugeben. Die meisten Compiler machen dann naemlich auch das, was man moechte.
    Jetzt fragst du dich vielleicht, was das ueberhaupt soll, den Code auf mehrere Dateien aufzuteilen (abgesehen davon, dass es uebersichtlicher ist). Das wird interessant, wenn man mal spaeter sehr viel Code auf einmal hat. Dann dauert das Uebersetzen naemlich teilweise richtig lange. Wenn man dann irgendwo was aendert, will man nicht alles neu uebersetzen muessen. Muss man auch nicht! Man hat ja hoffentlich voraus gedacht und die Objektdateien aufgehoben. So muss man bloss eine neue Objektdatei fuer die geaenderte Quellcodedatei erstellen und kann dann sofort linken. Man muss also bloss Buch fuehren, welche Objektdatei wann aus welchem Code erstellt wurde und kann dann sehr viel Zeit sparen. Klingt zunaechst nach viel Buchhaltungsarbeit, aber fuer so etwas wurden ja Computer erfunden 🙂 . Dieser letzte Punkt ist daher wieder etwas, was dir gerne von jeder IDE abgenommen wird und auch wenn du von Hand uebersetzt gibt es da sehr maechtige Hilfsmittel.

    Der vorhergehende Absatz ueber das Uebersetzen von einzelnen Dateien wird dich jetzt vermutlich noch nicht interessieren, das ist bloss ein Ausblick fuer spaeter. Du musst jetzt nur gucken, wie du in deiner IDE mehrere Dateien zu einem Projekt zusammen fasst oder wie du dem Compiler (der in der Regel auch den Linker aufruft), mehrere Dateien gleichzeitig uebergibst.

    *: Das bedeutet nicht, dass er den Code der Funktionen an der Stelle des Aufrufs einsetzt. Es ist eher so, dass im Objektcode noch Informationen fehlen oder noch Zusatzinformationen fuer den Linker drin sind, die vom Linker eingesetzt oder entfernt werden, weil erst beim Linkvorgang klar ist, was genau an der Stelle eingesetzt werden muss.



  • Danke für deine Erklärung SeppJ!
    Problem erledigt! Irgendwas stimmte mit meiner .pro oder mit meiner pro.user Datei für Qt Creator nicht. Ich hab sie daraufhin gelöscht um dann die Projektdatei neu anzulegen mit:

    qmake -project
    

    Danach konnte ich das Projekt wieder in der IDE compilieren.

    SeppJ, kann es sein das du was gegen Umlaute hast? 😃


Anmelden zum Antworten