Was ist die Dynamik der C ++ Delete-Anweisung?
-
27-09-2019 - |
Frage
Dies ist nur für Neugier willen, weil ich nicht new
und delete
in C ++ mit Ausnahme der einfachsten Anwendungen verwendet haben.
Ich weiß, dass delete
Mitteilungen Speicher. Das, was ich frage mich, ist wie funktioniert es komplexere Fälle behandeln?
Zum Beispiel, wenn ich eine benutzerdefinierte Klasse wie folgt aus:
class MyClass
{
public:
MyClass();
~MyClass()
{
delete [] intArray;
}
//public members here
private:
int* intArray;
};
Unter der Annahme der Klasse zuordnet Speicher irgendwie für intArray
, und es dann in den destructor loslassen, Was passiert, wenn ich die Klasse wie folgt verwendet: MyClass* myClass = new MyClass();
und es später wieder freigelassen mit delete myclass;
Wie delete
die Freigabe aller Speicher umgehen? Hat der Klassendestruktor erste der gesamte Speicher von der Klasse zugewiesen freizugeben aufgerufen (dh int* intArray
) und dann die Speicher zugewiesen, die Klasse zu halten?
Was passiert, wenn ich hatte eine Klasse wie folgt:
class MyClass
{
public:
MyClass();
~MyClass()
{
delete anotherMyClass;
}
//public members here
private:
MyClass* anotherMyClass;
};
anotherMyClass
Unter der Annahme, nicht mit dem Konstruktor zugeordnet, die Speicher sehr schnell aufbrauchen würde, was, wenn es eine Kette von MyClasses war miteinander verbunden wie eine verknüpfte Liste? Würde die Delete-Anweisung in der destructor Arbeit in diesem Fall? Würde jeder anotherMyClass
rekursiv gelöst werden, wenn der Destruktor aufgerufen wird?
Gibt es spezifische seltsame Tricks oder Einsprüche mit den new
und delete
Anweisungen, die Sie kennen?
Lösung
ein Zeiger (p
) ein dynamisch zugewiesenen Objekt gegeben, delete
macht zwei Dinge:
- Sie ruft den destructor des dynamisch zugewiesenen Objekts. Beachten Sie, dass, wenn
~MyClass()
abgeschlossen ist, werden die Destruktoren für alle Membervariablen der Klasse-Typ bezeichnet. - Es gibt den Speicher durch das dynamisch zugewiesenen Objekt besetzt.
Es sucht nicht die Elementvariablen des Objekts für andere Zeiger frei; es macht keinen anderen Speicher frei und tut nichts anderes.
Wenn Sie die Speicher von intArray
darauf befreien müssen, müssen Sie es in dem destructor von delete
MyClass
.
Doch in fast allen C ++ Code, haben Sie Sorge über diese nicht benötigen. Sie sollten automatisch verwalten dynamisch zugewiesenen Objekte mit Smart-Pointer wie shared_ptr
, unique_ptr
, auto_ptr
und scoped_ptr
sein. Manuelles Ressourcenmanagement ist bestenfalls schwierig und soll nach Möglichkeit vermieden werden.
Dies ist Teil einer breiteren Idiom ist, Scope-Bound Resource Management (SBRM, auch Ressourcen Acquisition genannt ist Initialisierung oder RAH). Dies ist bei weitem die wichtigste Design-Muster zu verstehen und überall in Ihrem C ++ Code zu verwenden.
Wenn in der Klasse Sie diese stattdessen erklärt hatte:
boost::scoped_ptr<int> intArray;
dann, wenn das scoped_ptr<int>
Objekt zerstört wird, wird es den Zeiger freizugeben, dass es hält. Sie müssen dann keine Arbeit nicht tun, um das Objekt manuell zu zerstören.
In gut geschriebenen, modernen C ++ Code, sollten Sie nur selten manuell Verwendung delete
müssen. Smart-Pointer und andere SBRM Behälter sollen jede Art von Ressource zu verwalten verwendet werden, die Bereinigung benötigt, einschließlich dynamisch zugewiesenen Objekte.
In Ihrem zweiten Beispiel gegeben eine verkettete Liste, das aussieht wie:
x -> y -> z -> 0
würden Sie eine Reihenfolge der Operationen haben das wie folgt aussieht:
delete x;
x.~MyClass();
delete y;
y.~MyClass();
delete z;
z.~MyClass();
delete 0;
[free memory occupied by z]
[free memory occupied by y]
[free memory occupied by x]
Die Objekte in der verknüpften Liste würden in umgekehrter Reihenfolge zerstört werden.
Andere Tipps
delete intArray;
gehe ich davon aus intArray
zeigt auf das erste Element eines int
Array? In diesem Fall ergibt delete intArray
nicht definiertes Verhalten. Wenn Sie zuteilen über new[]
, Sie muss Freigabe über delete[]
.
delete[] intArray;
Ja, ich weiß, delete intArray
Macht erscheinen zu gut funktionieren auf bestimmten Systemen mit bestimmten Compiler Versionen unter bestimmten Compiler-Optionen - oder auch nicht. Das ist undefiniertes Verhalten für ya.
Außerdem sollten Sie die Rule of Three folgen. Ihren eigenen destructor definieren, sondern auf dem implizit definierten Copykonstruktor und kopiert Zuweisungsoperator unter Berufung ist ein Rezept für eine Katastrophe.