Doppel Kostenlose oder Korruption nach Warteschlange::push
#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(t);
}
Nachdem ich diesen starte, bekomme ich eine runtime error "double free " oder Korruption". Wenn ich loszuwerden, der Destruktor Inhalt (die delete
) funktioniert es einwandfrei. Was ist falsch?
- Read this und dann read this.
Test t();
eine Funktion deklariert, der code wird nicht kompilieren. Sie müssen die post-code.- Während Sie die Diashow in das zweite Glied hat einige gute Punkte, damit bin ich nicht einverstanden mit Ihnen allen, insbesondere dem Beispiel auf Seite 9 zeigt einen lokalen großen Objekts zurückgegebenen Wert. Das ruft copy-Konstruktion, die kann teuer werden, es sei denn, Sie sind mit C++11 move-Semantik.
- Die Diashow spricht über intelligente Zeiger wie
std::unique_ptr
, die Teil der C++11 standard. Ich denke, es ist sicher anzunehmen ist, impliziert move-Semantik, zumindest, wenn nicht RVO. - Lesen Sie in der Regel aus Drei
std::unique_ptr
ist neu in C++11, aberstd::shared_ptr
ist nicht. Auch die Diashow erwähntstd::array
, das ist nicht neu in C++11, entweder. Also ich glaube nicht, dass move-Semantik angenommen werden kann. Auch mit RVO verwendet, kopieren Zuordnung würde aufgerufen werden, ohne C++11 move-Zuweisung.- Beide
std::shared_ptr
undstd::array
sind nur C++11. Möglicherweise gibt es ähnliche Dinge in der boost, aber diese würde nicht haben einestd::
Präfix. - tatsächlich, Sie wurden eingeführt, TR1 und dann aktualisiert werden, in C++11 zu passen-boost-Funktionalität.
- Fair genug. Ich habe Sie auch nie verwenden, TR1, aber ich sehe Ihren Punkt. Sogar dann, allerdings, wäre es in der
tr1
namespace, nichtstd
😉 - gut gespielt 😛
- Tut dies selbst kompilieren? Sollte nicht
Test t();
werdenTest t
? - Behoben. Es kompiliert und hat das gleiche problem jetzt.
- Leider sind die Antworten unten sind der falsche Weg, um dieses problem zu beheben. Sollten Sie
myArray
einestd::vector<int>
eher als eineint*
dann alle Probleme gelöst sind (inklusive der korrekten C++ - move-Konstruktoren enthalten sind, die in std::vector).
Du musst angemeldet sein, um einen Kommentar abzugeben.
Reden wir über das kopieren von Objekten in C++.
Test t;
ruft den Standard-Konstruktor, der reserviert eine neue Integer-array. Das ist in Ordnung, und Ihr Verhalten zu erwarten.Problem kommt, wenn Sie drücken Sie
t
in Ihre Warteschlange mitq.push(t)
. Wenn du bist vertraut mit Java, C#, oder fast jedes andere Objekt-orientierte Sprache, die man erwarten kann, ist das Objekt, das Sie erstellt weiter vorn werden der Warteschlange Hinzugefügt, aber C++ funktioniert das nicht so.Wenn wir einen Blick auf
std::queue::push
Methode, sehen wir, dass das element, wird der Warteschlange Hinzugefügt wird initialisiert", um eine Kopie von x ist. Es ist eigentlich Marke neue Objekt mit der copy-Konstruktor zu duplizieren jedes Mitglied Ihrer ursprünglichenTest
- Objekt, um eine neueTest
.C++ - compiler generiert copy-Konstruktor wird für Sie standardmäßig! Das ist ziemlich praktisch, verursacht aber Probleme mit der Zeiger-Mitglieder. In deinem Beispiel, denken Sie daran, dass
int *myArray
ist nur eine Speicher-Adresse; wenn der Wert desmyArray
ist kopiert aus dem alten Objekt auf das neue ein, Sie haben nun zwei Objekte, die auf das gleiche array im Speicher. Dies ist nicht intrinsisch schlecht, aber der Destruktor wird dann versuchen, zu löschen, die das gleiche array zweimal, damit das "double free " oder Korruption" runtime error.Wie kann ich es beheben?
Der erste Schritt ist die Einführung einer copy-Konstruktor, können Sie sicher kopieren Sie die Daten von einem Objekt zum anderen. Für Einfachheit, es könnte in etwa so Aussehen:
Nun, wenn Sie das kopieren von Test-Objekten, ein neues array zugewiesen werden, für das neue Objekt, und die Werte des Arrays kopiert wird.
Sind wir nicht völlig aus dem ärger noch, obwohl. Es gibt eine andere Methode, die der compiler generiert für Sie dazu führen könnte, dass ähnliche Probleme - Zuordnung. Der Unterschied ist, dass mit der Zuordnung, wir haben bereits ein vorhandenes Objekt, dessen Speicher muss verwaltet werden, entsprechend. Hier ist eine grundlegende Zuweisungsoperator Umsetzung:
Ist der wichtige Teil hier ist, dass wir kopieren die Daten aus dem anderen array in das Objekt-array, das halten jedes Objekt Speicher trennen. Wir haben auch einen check für die selbst-Zuordnung; sonst würden wir kopieren uns selbst zu uns selbst, die können einen Fehler auslösen (nicht sicher, was es tun soll). Wenn wir das löschen und die Zuweisung von mehr Speicher, die selbst-Zuweisung überprüfen, die uns daran hindert, löschen den Speicher, aus dem wir brauchen, um zu kopieren.
Test t();
ist keine Deklaration, sondern eine vorwärts-Deklaration einer Funktion.myArray
zustd::vector<int>
. Auf diese Weise erhalten Sie richtig alle die mehr effiziente verschieben von Komponenten (Konstruktor/Zuordnung), die definiert sind in C++11.other
's Destruktor. Wenn Sie wollen, um tatsächlich das array zwischen den Objekten, dann möchten Sie vielleicht einen Blick in move-Semantik: stackoverflow.com/questions/3106110/what-are-move-semanticsmemcpy
kopiert bytes nicht zahlen. Besserstd::copy
(nur so effizient, aber wesentlich sicherer).Das problem ist, dass die Klasse enthält eine verwaltete RAW-pointer, aber nicht implementieren Sie die Regel der drei (fünf in C++11). Als Ergebnis bekommen Sie (erwartungsgemäß) eine doppelte löschen, weil Sie zu kopieren.
Wenn Sie lernen, Sie sollten lernen, wie implementieren Sie die Regel der drei (fünf). Aber das ist nicht die richtige Lösung für dieses problem. Sie sollten mit standard-container-Objekte, anstatt zu versuchen, verwalten Sie Ihre eigenen internen Behälter. Die genauen container wird davon abhängen, was Sie versuchen zu tun, aber std::vector ist ein guter Standardwert (und Sie können anschließend, wenn es nicht opimal).
Dem Grund sollten Sie die Verwendung eines standard-Containers ist die
separation of concerns
. Ihre Klasse betroffen sein, die entweder mit business-Logik und Ressourcen-management (nicht beide). VorausgesetztTest
ist Klasse, die Sie verwenden, um irgendeinen Staat über Ihr Programm, dann ist es die Geschäftslogik und sollte es nicht tun Ressource management. Wenn auf der anderen SeiteTest
soll zu verwalten, ein array, dann müssen Sie wahrscheinlich, um mehr zu erfahren über das, was sich innerhalb der standard-Bibliothek.Sind Sie immer Doppel Kostenlose oder Korruption, weil zuerst der Destruktor für Objekt q in diesem Fall den Speicher neue frei.Das nächste mal, wenn detructor wird aufgerufen, Objekt t damals der Speicher schon frei (gemacht für q), also, wenn im Destruktor delete[] myArray; ausgeführt wird, wird es werfen Doppel Kostenlose oder Korruption.
Der Grund ist, dass sowohl Objekt-sharing den gleichen Speicher, so definieren \Kopie, Zuweisung und gleich-operator wie bereits in der obigen Antwort.
Müssen Sie definieren, ein copy-Konstruktor, Zuweisungs-operator.
Ihre Kopie, schob Sie auf die Schlange verweist auf den gleichen Speicher Ihre ursprüngliche ist. Wenn der erste ist zerstört, löscht den Speicher. Die zweite zerstört und versucht, Sie zu löschen den gleichen Speicher.
Können Sie auch überprüfen, null vor dem löschen, so dass
oder definieren Sie alle delete-Operationen ina sichere Art und Weise, wie diese:
und verwenden Sie dann
delete
mit einem null-Zeiger nicht tun. Auch die Einstellung der Zeiger auf null nach dem löschen ist redundant in ein Destruktor, weil es für ungültig erklärt werden, wenn die Funktion endet.Ähm, sollte nicht den Destruktor aufrufen löschen, statt delete[]?
new[]
, ergo braucht es einedelete[]
.delete [] myArray
. Sehen Sie die neuen, es ist nichtnew int
abernew int [x]
.