Double free oder Korruption nach queue::push

Lesezeit: 7 Minuten

Double free oder Korruption nach queuepush
Mihai Neacsu

#include <queue>
using namespace std;

class Test{
    int *myArray;

        public:
    Test(){
        myArray = new int[10];
    }

    ~Test(){
        delete[] myArray;
    }

};


int main(){
    queue<Test> q
    Test t;
    q.push
}

Nachdem ich dies ausgeführt habe, erhalte ich einen Laufzeitfehler “doppelt frei oder beschädigt”. Wenn ich den Destruktorinhalt (die delete) es funktioniert gut. Was ist los?

  • Lesen Sie dies und dann lesen Sie dies.

    – Chris

    28. Dezember 12 um 2:22 Uhr


  • Test t(); eine Funktion deklariert, wird Ihr Code nicht kompiliert. Sie müssen echten Code posten.

    – billz

    28. Dezember 12 um 2:29 Uhr


  • Lesen Sie die Dreierregel

    – Martin York

    28. Dezember 12 um 2:33 Uhr

  • @RemyLebeau, fair genug. Ich habe TR1 nie benutzt, aber ich verstehe Ihren Punkt. Aber selbst dann wäre es in der tr1 Namensraum, nicht std 😉

    – Chris

    28. Dezember 2012 um 2:45 Uhr

  • Leider sind die unten angegebenen Antworten der falsche Weg, um dieses Problem zu beheben. Du solltest machen myArray ein std::vector<int> eher als ein int* dann sind alle Probleme gelöst (einschließlich der korrekten C++-Move-Konstruktoren, die in std::vector enthalten sind).

    – Martin York

    28. Dezember 12 um 5:54 Uhr

Double free oder Korruption nach queuepush
derekerdmann

Lassen Sie uns über das Kopieren von Objekten in C++ sprechen.

Test t;, ruft den Standardkonstruktor auf, der ein neues Array von Ganzzahlen zuweist. Das ist in Ordnung und Ihr erwartetes Verhalten.

Ärger kommt, wenn du drückst t in Ihre Warteschlange mit q.push

Wenn wir einen Blick darauf werfen std::queue::push Methode, sehen wir, dass das Element, das der Warteschlange hinzugefügt wird, „mit einer Kopie von x initialisiert“ wird. Es ist eigentlich ein brandneues Objekt, das den Kopierkonstruktor verwendet, um jedes Mitglied Ihres Originals zu duplizieren Test Objekt, um ein neues zu machen Test.

Ihr C++-Compiler generiert standardmäßig einen Kopierkonstruktor für Sie! Das ist ziemlich praktisch, verursacht aber Probleme mit Zeigermitgliedern. Denken Sie in Ihrem Beispiel daran int *myArray ist nur eine Speicheradresse; wenn der Wert von myArray vom alten Objekt in das neue kopiert wird, haben Sie jetzt zwei Objekte, die auf dasselbe Array im Speicher zeigen. Dies ist an sich nicht schlecht, aber der Destruktor versucht dann, dasselbe Array zweimal zu löschen, daher der Laufzeitfehler "doppelt frei oder beschädigt".

Wie kann ich es beheben?

Der erste Schritt ist die Implementierung von a Konstruktor kopieren, die die Daten sicher von einem Objekt auf ein anderes kopieren kann. Der Einfachheit halber könnte es so aussehen:

Test(const Test& other){
    myArray = new int[10];
    memcpy( myArray, other.myArray, 10 );
}

Wenn Sie jetzt Testobjekte kopieren, wird dem neuen Objekt ein neues Array zugewiesen, und die Werte des Arrays werden ebenfalls kopiert.

Wir sind aber noch nicht ganz aus der Klemme. Es gibt noch eine andere Methode, die der Compiler für Sie generiert und die zu ähnlichen Problemen führen könnte – die Zuweisung. Der Unterschied besteht darin, dass wir bei der Zuweisung bereits ein vorhandenes Objekt haben, dessen Speicher entsprechend verwaltet werden muss. Hier ist eine grundlegende Implementierung eines Zuweisungsoperators:

Test& operator= (const Test& other){
    if (this != &other) {
        memcpy( myArray, other.myArray, 10 );
    }
    return *this;
}

Der wichtige Teil hier ist, dass wir die Daten aus dem anderen Array in das Array dieses Objekts kopieren und den Speicher jedes Objekts getrennt halten. Wir haben auch einen Scheck für die Selbstzuweisung; Andernfalls würden wir von uns selbst zu uns selbst kopieren, was möglicherweise einen Fehler auslöst (nicht sicher, was es tun soll). Wenn wir mehr Speicher löschen und zuweisen, hindert uns die Selbstzuweisungsprüfung daran, Speicher zu löschen, aus dem wir kopieren müssen.

  • Ich habe die ursprüngliche Frage korrigiert. Also habe ich das auch behoben Test t(); ist keine Variablendeklaration, sondern eine Vorwärtsdeklaration einer Funktion.

    – Martin York

    28. Dezember 12 um 5:49 Uhr

  • Korrekte Erklärung, was schief läuft und wie es behoben werden kann (falls Sie es beheben wollten). Aber es ist die falsche Lösung des Problems. Die richtige Lösung in jedem echten Code besteht darin, den Typ des Members zu ändern myArray zu std::vector<int>. Auf diese Weise erhalten Sie korrekterweise alle effizienteren Move-Komponenten (Konstruktor/Zuweisung), die in C++11 definiert sind.

    – Martin York

    28. Dezember 12 um 5:56 Uhr

  • @LokiAstari - Guter Punkt, aber ich denke, es ist wichtiger, das eigentliche Problem zu veranschaulichen und wie man es traditionell behebt. Wenn man das verstanden hat, wird klar, warum der Vektor die bessere Lösung ist.

    – derekerdmann

    28. Dezember 12 um 6:58 Uhr

  • Würden Sie sagen, dass der Compiler eine flache Kopie im Gegensatz zu einer tiefen Kopie erstellt hat, was der Code beabsichtigte?

    – viki.omega9

    26. März 2014 um 00:23 Uhr

  • @viki.omega9 - Ja, es ist eine flache Kopie. Der wichtige Teil ist, wie das Verhalten des Compilers diese Art von Speicherfehler verursacht, da tiefe Kopien nicht immer das gewünschte Ergebnis sind.

    – derekerdmann

    26. März 14 um 0:35 Uhr

1643916609 709 Double free oder Korruption nach queuepush
Martin York

Das Problem besteht darin, dass Ihre Klasse einen verwalteten RAW-Zeiger enthält, aber die Dreierregel (fünf in C++11) nicht implementiert. Als Ergebnis erhalten Sie (erwartet) eine doppelte Löschung wegen des Kopierens.

Wenn Sie lernen, sollten Sie lernen, wie man die Regel von drei (fünf) umsetzt. Aber das ist nicht die richtige Lösung für dieses Problem. Sie sollten Standard-Containerobjekte verwenden, anstatt zu versuchen, Ihren eigenen internen Container zu verwalten. Der genaue Container hängt davon ab, was Sie zu tun versuchen, aber std::vector ist ein guter Standard (und Sie können ihn nachträglich ändern, wenn er nicht optimal ist).

#include <queue>
#include <vector>

class Test{
    std::vector<int> myArray;

    public:
    Test(): myArray(10){
    }    
};

int main(){
    queue<Test> q
    Test t;
    q.push
}

Der Grund, warum Sie einen Standardbehälter verwenden sollten, ist der separation of concerns. Ihre Klasse sollte sich entweder mit Geschäftslogik oder Ressourcenmanagement befassen (nicht mit beidem). Vorausgesetzt Test eine Klasse ist, die Sie verwenden, um einen Zustand Ihres Programms aufrechtzuerhalten, dann ist es Geschäftslogik und sollte keine Ressourcenverwaltung durchführen. Wenn andererseits Test ein Array verwalten soll, müssen Sie wahrscheinlich mehr darüber erfahren, was in der Standardbibliothek verfügbar ist.

Sie bekommen doppelt frei oder Korruption weil der erste Destruktor für das Objekt ist Q in diesem Fall der von zugewiesene Speicher Neu wird frei sein. Das nächste Mal, wenn Detructor für ein Objekt aufgerufen wird T zu diesem Zeitpunkt ist der Speicher bereits frei (fertig für q), also wenn im Destruktor löschen[] meinArray; wird ausführen wird es werfen doppelt frei oder Korruption. Der Grund ist, dass beide Objekte denselben Speicher teilen, also copy, Zuweisung und Gleichheitsoperator definieren, wie in der obigen Antwort erwähnt.

1643916609 999 Double free oder Korruption nach queuepush
Ryan Guthrie

Sie müssen einen Kopierkonstruktor, eine Zuweisung und einen Operator definieren.

class Test {
   Test(const Test &that); //Copy constructor
   Test& operator= (const Test &rhs); //assignment operator
}

Ihre Kopie, die in die Warteschlange geschoben wird, zeigt auf denselben Speicher wie Ihr Original. Wenn der erste zerstört wird, löscht er den Speicher. Der zweite zerstört und versucht, denselben Speicher zu löschen.

Sie können auch versuchen, null vor dem Löschen zu überprüfen

if(myArray) { delete[] myArray; myArray = NULL; }

oder Sie können alle Löschvorgänge auf sichere Weise wie folgt definieren:

#ifndef SAFE_DELETE
#define SAFE_DELETE(p) { if(p) { delete (p); (p) = NULL; } }
#endif

#ifndef SAFE_DELETE_ARRAY
#define SAFE_DELETE_ARRAY(p) { if(p) { delete[] (p); (p) = NULL; } }
#endif

und dann verwenden

SAFE_DELETE_ARRAY(myArray);

  • Das Prüfen auf null ist wegen des Aufrufens überflüssig delete mit einem Nullzeiger tut nichts. Auch das Setzen des Zeigers auf null nach dem Löschen ist in einem Destruktor überflüssig, da er ungültig wird, wenn die Funktion endet.

    – Galik

    13. Mai 19 um 0:16 Uhr


1643916610 177 Double free oder Korruption nach queuepush
Benutzer888379

Ähm, sollte der Destruktor nicht delete statt delete aufrufen[]?

  • Das Prüfen auf null ist wegen des Aufrufens überflüssig delete mit einem Nullzeiger tut nichts. Auch das Setzen des Zeigers auf null nach dem Löschen ist in einem Destruktor überflüssig, da er ungültig wird, wenn die Funktion endet.

    – Galik

    13. Mai 19 um 0:16 Uhr


.

759260cookie-checkDouble free oder Korruption nach queue::push

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy