#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?
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
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:
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:
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
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.
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:
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
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
.
7592600cookie-checkDouble free oder Korruption nach queue::pushyes
This website is using cookies to improve the user-friendliness. You agree by using the website further.
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, nichtstd
😉– 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
einstd::vector<int>
eher als einint*
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