double free or corruption



  • Hallo zusammen,

    mir raubt gerade der im Titel benannte Fehler den letzten Nerv. Hoffentlich könnt ihr mir helfen:

    Ich habe eine Klasse Token, die wiederum ein Bild enthält. Da nicht jedes Token ein Bild hat, wird dieses beim durch den Standardkonstruktor von Token mit 0 initialisiert. Nun mache ich folgende Beobachtung:

    vector<Token> tokenstream;
    Token a(0); //kein Fehler
    tokenstream.push_back(Token(0)); //Fehler
    

    Kann mir jemand sagen, warum ich keine Tokens in einem vector speichern kann?

    Vielen Dank
    LG, freakC++



  • Der Fehler liegt woanders. Du spielst sicher irgendwo mit Pointern rum und das geht in die Hose. Die Auswirkungen zeigen sich dann erst später im Programmablauf.



  • mmhh..eigentlich nicht. Ich baue nur einmal ein 2D-Array auf. Ich poste mal den restlichen Code

    class PictureShape : public Shape {
    	private:
    		int height;
    		int width;
    		bool** pic;
    
    		void init();
    
    	public:
    		PictureShape(int, int, int, int, int, int); //Rechteck
    		PictureShape(int, int); //unspezifiziert
    
    		~PictureShape() {
    			for (int i = 0; i < width ; i++)
           				 delete [] pic[i] ;
       			 delete [] pic;
    		}
    
    		PictureShape operator!(void);
    		PictureShape operator&(const PictureShape&);
    		void operator=(const PictureShape&);
    		PictureShape operator|(const PictureShape&);
    
    		virtual bool isWhite(int x, int y) const {
    			if(pic[x][y] == true)
    				return true;
    			else 
    				return false;
    		}
    
    };
    
    PictureShape::PictureShape(int w, int h) : height(h), width(w) {
    	init();
    }
    
    void PictureShape::init()  {
    	pic = new bool*[width];
    	for (int i = 0; i < width ; i++)
    		pic[i] = new bool[height];
    	for(int i = 0; i < width; i++)	
    		for(int j = 0; j < height; j++)
    			pic[i][j] = false;	
    }
    
    //---------------------------------------------------------
    class Token
    {
        private:
            PictureShape myForm;
            int myValue;
    
        public:
    	Token(int); //kein Form
    
            int getValue() const { return myValue; }
    };
    
    Token::Token(int val) : myForm(0,0) {
    	myValue = val;
    }
    //---------------------------------------------------------
    

    Das ist alles. Ich weiß da überhaupt nicht weiter...


  • Mod

    freakC++ schrieb:

    mmhh..eigentlich nicht.

    Inwiefern sind denn Doppelzeiger und ein Programm voller new und delete keine Herumspielerei mit Pointern? Und klar geht das in die Hose. Wenn man das schon eine ressourcenhaltende Überklasse schreibt (was man nicht tun sollte), dann sollte man auch Grundregeln wie die Regel der großen Drei kennen.



  • Ok, mir war nicht bewusst, dass man solch etwas nicht tun sollte.

    Wie würdet ihr denn die Daten kapseln? Die new und delete beziehen sich ja nur auf das Erstellung und Löschen des 2D Arrays. Ich orientiere mich nämlich an der FAQ dieses Forums zum Parserbau. Darin wird auch eine Klasse "Token" erstellt, die den Tokenwert und die eigentliche Bedeutung hält.

    Ich habe den Fehler übrigens mal zurückverfolgt. Aus irgendeinem Grund tritt beim Konstruktor der Fehler aus:

    PictureShape::PictureShape(int w, int h) {
    	height = 0; //Sobald ich height und width mit anderen Zahlen als 0 initialisiere, kommt ein "Core dumped" - Fehler
    	width = 0;
    //	init();    //Ich habe init() mal auskommenteirt. Somit wird nun nichts mit dem 2D Array gemacht
    }
    


  • Hast du SeppJ nicht verstanden? Regel der Großen Drei
    Wenn du einen Kopierkonstruktor definierst, hast du zumindest ein Problem behoben.

    Die bessere Lösung wäre, das zweidimensionale Array mit einem vector umzusetzen, dann kannst du auf solche Regeln verzichten.



  • Ich hatte gerade wieder ein kleines WTF-Erlebnis.
    Noch nie was von STL-Containern gehört?
    Ich sehe da als beste Möglichkeit den gesamten Code gleich neu zu schreiben.

    virtual bool isWhite(int x, int y) const {
        if(pic[x][y] == true)
            return true;
        else 
            return false;
    }
    

    Aha.. wieso nicht einfach

    virtual bool isWhite(int x, int y) const 
    {
         return pic[x][y];
    }
    

  • Mod

    Den Fehler habe ich dir doch schon genannt: Was passiert, wenn eine Kopie deines Objektes angelegt wird? Dann haben beide Kopien den gleichen Zeiger und wenn sie zerstört werden, führen beide ein delete auf diesen Zeiger aus. Und jetzt verknüpf diese Beschreibung mal mit dem Wortlaut deiner Fehlermeldung 🙂 .

    Gegenmaßnahmen:
    a: Direkte Fehlerkorrektur: Regel der großen Drei beachten.
    b: Gar nicht erst machen: Inwiefern unterscheidet sich dein Pointergefrickel von vector<vector>? Von der funktion her gar nicht, der Unterschied ist, dass deines falsch programmiert ist, vector<vector> jedoch funktionieren würde.
    c: Nachdenken: Brauchst du das überhaupt? Du hast eine effektiv eindimensionale Struktur, einzig deine Interpretation macht sie 2D. Das heißt, du kommst eigentlich mit einem 1D-vector aus, wenn du die Indizes selber berechnest. Das wäre ungleich effizienter als deine jetzige Lösung.



  • Ok 😃 Ich glaube, ich werde versuchen, das 2D-Array als 1D Vector darzustellen und die Koordinaten selbst zu berechnen.

    @Sone: Gehört schon; damit gearbeitet == nein

    Nur so aus Interesse: Die Erklärung bezüglich der Pointer von SeppJ leuchtet mir ein. Bedeutet das, dass ich im Kopierkonstruktor dann das ganze 2D-Array neu anlegen müsste und so kopieren muss? Ich darf ja nicht einfach die Zeiger übergeben.

    Vielen Dank
    LG, freakC++



  • ja musst du. wenn du C++11 machst kannst du mit RValue Referenzen was tricksen, aber eigentlich musst du alles kopieren

    aber mal ne andere sache: du bist seit über 4 jahren hier im forum angemeldet, du hast auch fast 2000 beiträge. ich hatte dich mal als ganz guten C++'ler in erinnerung. irgendwas stimmt doch hier nicht, denn in den letzten wochen stellst du relativ einfache fragen (nicht allerunterste anfänger schublade, aber keine dinge, die man wirklich nicht wissen kann als anfänger`).....



  • Skym0sh0 schrieb:

    ja musst du. wenn du C++11 machst kannst du mit RValue Referenzen was tricksen, aber eigentlich musst du alles kopieren

    Ok. Ich habe es jetzt mit einem vector gemacht und damit scheint es zu funktionieren. Wie löst denn die Klasse vector das intern, dass es jetzt keine Probleme gibt. Werden da gar keine Zeiger benutzt?

    Skym0sh0 schrieb:

    aber mal ne andere sache: du bist seit über 4 jahren hier im forum angemeldet, du hast auch fast 2000 beiträge. ich hatte dich mal als ganz guten C++'ler in erinnerung. irgendwas stimmt doch hier nicht, denn in den letzten wochen stellst du relativ einfache fragen (nicht allerunterste anfänger schublade, aber keine dinge, die man wirklich nicht wissen kann als anfänger`).....

    Die Anzahl meiner Beiträge setzt sich wohl aus meinen C++-Anfängen durch. Das meiste habe ich mir alles selbst beigebracht und lange hatten meine Fragen das Niveau "Warum muss man Variablen deklarieren" (siehe C++ Borland Forum). Dann war Pause...ich machte mein Abi und fing an zu studieren. Im Matheforum gin es weiter mit dem Sammeln von Beiträgen und jetzt fange ich aufgrund meines Studiums wieder mit C++ an. Ich habe nun lange Java programmiert, doch reizt mich C++ mehr. Ich bin kein hervorragender Programmierer, der sicherlich noch einige Grundlagen erlernen muss. Wir haben ganze zwei Vorlesungen C++ gehört und sollen nun einen kleinen Compiler schreiben 😮 . Wissen erhalten wir also nur durch Ausprobieren und Nachfragen. Daher verzeiht mir, wenn ich Grundlagendinge frage!

    Danke für die immer gute Hilfe!
    LG, freakC++


  • Mod

    freakC++ schrieb:

    Nur so aus Interesse: Die Erklärung bezüglich der Pointer von SeppJ leuchtet mir ein. Bedeutet das, dass ich im Kopierkonstruktor dann das ganze 2D-Array neu anlegen müsste und so kopieren muss? Ich darf ja nicht einfach die Zeiger übergeben.

    Prinzipiell kannst du natürlich auch ganz was anderes machen, aber das wäre die übliche Methode, weil es dem entspricht, was man von einer Kopie erwartet. Eine andere Methode, die hin und wieder vorkommt, wäre das komplette Verbieten von Kopien (das erreicht man, indem man den Kopierkonstruktor private macht oder (in C++11) deleted).

    Allgemein sollte in "modernem" C++ gelten: Falls du tatsächlich Klassen schreibst, die Ressourcen halten, dann sollten sie auch nur dies tun. Deine PictureShape braucht gar nichts über Speicherverwaltung zu wissen, dass ist bestimmt nicht, was eine PictureShape auszeichnet. Wenn du die Ressourcen selber verwalten willst (es gibt ja fertige Klassen in der Standardbibliothek dafür, aber die hat ja auch irgendwann mal jemand geschrieben), dann solltest du das so anlegen, dass jedes Objekt höchstens eine eigene Ressource verwaltet.

    Das hieße hier, du schreibst dir eine 2D-Arrayklasse, die von der PictureShape benutzt wird. Die 2D-Arrayklasse hat die Hauptaufgabe, intern Indizes zu verbiegen, um eine 2D-Struktur auf eine 1D-Struktur zu mappen. Allgemein ist eine solche Klasse sehr nützlich, das heißt, wenn du dir nun einmal Mühe gibst und die 2D-Arrayklasse richtig schön abstrakt hältst (Templates und so), dann wirst du diese in Zukunft immer wieder benutzen können.
    Die 2D-Arrayklasse verwaltet aber auch nicht ihren eigenen Speicher, schließlich ist ihre Hauptaufgabe das verbiegen von Indizes. Der konkrete Speicher sollte daher in einer dynamischen Arrayklasse gehalten werden. Die Standardbibliothek kennt bereits solch eine Klasse (std::vector) daher bietet es sich an, diese zu benutzen. Falls du es selber schreiben wolltest, wäre dies dann die Ebene, auf der tatsächlich new und delete auftauchen würden (oder wenn du es so abstrakt wie der vector machst: allocate und deallocate einer allgemeinen Allokatorklasse).



  • freakC++ schrieb:

    Skym0sh0 schrieb:

    ja musst du. wenn du C++11 machst kannst du mit RValue Referenzen was tricksen, aber eigentlich musst du alles kopieren

    Ok. Ich habe es jetzt mit einem vector gemacht und damit scheint es zu funktionieren. Wie löst denn die Klasse vector das intern, dass es jetzt keine Probleme gibt. Werden da gar keine Zeiger benutzt?

    klar, aber der vector kümmert sich wirklich NUR um diesen speicehr und den zugriff darauf, und nicht noch um die inhalte die da liegen.

    freakC++ schrieb:

    Skym0sh0 schrieb:

    aber mal ne andere sache: du bist seit über 4 jahren hier im forum angemeldet, du hast auch fast 2000 beiträge. ich hatte dich mal als ganz guten C++'ler in erinnerung. irgendwas stimmt doch hier nicht, denn in den letzten wochen stellst du relativ einfache fragen (nicht allerunterste anfänger schublade, aber keine dinge, die man wirklich nicht wissen kann als anfänger`).....

    Die Anzahl meiner Beiträge setzt sich wohl aus meinen C++-Anfängen durch. Das meiste habe ich mir alles selbst beigebracht und lange hatten meine Fragen das Niveau "Warum muss man Variablen deklarieren" (siehe C++ Borland Forum). Dann war Pause...ich machte mein Abi und fing an zu studieren. Im Matheforum gin es weiter mit dem Sammeln von Beiträgen und jetzt fange ich aufgrund meines Studiums wieder mit C++ an. Ich habe nun lange Java programmiert, doch reizt mich C++ mehr. Ich bin kein hervorragender Programmierer, der sicherlich noch einige Grundlagen erlernen muss. Wir haben ganze zwei Vorlesungen C++ gehört und sollen nun einen kleinen Compiler schreiben 😮 . Wissen erhalten wir also nur durch Ausprobieren und Nachfragen. Daher verzeiht mir, wenn ich Grundlagendinge frage!

    nix verzeihung, dafür sind wir ja da und es macht ja auch spass (sonst wäre keiner hier am antworten).
    dass du fragst ist sehr gut wie ich finde und auch wichtig (es gibt immer wen der die gleiche frage hat und dem ist später geholfen dadurch). ich bin ja selbst auch kein profi und gehe probleme auch oft (fast immer) von der total falschen seite an, von daher mach dir hier mal keine sorgen


Anmelden zum Antworten