Ich versuche, "große drei" in C ++ zu lernen. Ich habe es geschafft, sehr einfaches Programm für "große drei" zu machen .. aber ich bin nicht sicher, wie man den Objektzeiger verwendet. Das folgende ist mein erstes Versuch.
Ich habe Zweifel, als ich das schrieb ...
Fragen
Stimmt es, dass ich den Zeiger im Destruktor löschen muss?
%Vor%Implementierung
%Vor%Vielen Dank im Voraus.
Stimmt es, dass ich den Zeiger im Destruktor löschen muss?
Wenn Sie ein Objekt wie dieses entwerfen, müssen Sie zuerst die Frage beantworten: Besitzt das Objekt den Speicher, auf den der Zeiger zeigt? Wenn ja, muss der Destruktor des Objekts offensichtlich diesen Speicher bereinigen, also muss er ja delete aufrufen. Und dies scheint Ihre Absicht mit dem gegebenen Code zu sein.
In manchen Fällen möchten Sie jedoch Zeiger haben, die sich auf andere Objekte beziehen, deren Lebensdauer von etwas anderem verwaltet werden soll. In diesem Fall möchten Sie nicht löschen, da dies Aufgabe eines anderen Teils des Programms ist. Darüber hinaus ändert das den gesamten nachfolgenden Entwurf, der in den Kopierkonstruktor und den Zuweisungsoperator eingeht.
Ich werde mit der Beantwortung der restlichen Fragen fortfahren, unter der Annahme, dass jedes TreeNode-Objekt Besitzrechte an den linken und rechten Objekten haben soll.
Ist dies der richtige Weg, den Standardkonstruktor zu implementieren?
Nein. Sie müssen die Zeiger left
und right
auf NULL initialisieren (oder 0, wenn Sie bevorzugen). Dies ist notwendig, da unintialisierte Zeiger einen beliebigen Wert haben können. Wenn Ihr Code jemals standardmäßig einen TreeNode erstellt und dann zerstört, ohne diesen Zeigern jemals etwas zuzuweisen, wird delete aufgerufen, unabhängig vom Anfangswert. Wenn also in diesem Design diese Zeiger auf nichts zeigen, müssen Sie sicherstellen, dass sie auf NULL gesetzt sind.
Wie weisen Sie die Zeigervariable im Kopierkonstruktor zu? Die Art, wie ich in Copy Constructor geschrieben habe, könnte falsch sein.
Die Zeile left = new TreeNode();
erstellt ein neues TreeNode-Objekt und setzt left
, um darauf zu zeigen. Die Zeile left = node.left;
weist diesen Zeiger neu zu, um auf das TreeNode-Objekt zu zeigen, auf das node.left
zeigt. Es gibt zwei Probleme damit.
Problem 1: Nichts zeigt jetzt auf diesen neuen TreeNode. Es ist verloren und wird zu einem Speicherleck, weil nichts es jemals zerstören kann.
Problem 2: Nun zeigen sowohl left
als auch node.left
auf den selben TreeNode. Das bedeutet, dass das Objekt, das kopiert wird, und das Objekt, von dem es Werte empfängt, beide denken, dass sie denselben TreeNode besitzen und beide in ihren Destruktoren delete aufrufen. Der zweimalige Aufruf von delete für dasselbe Objekt ist immer ein Fehler und führt zu Problemen (einschließlich möglicher Abstürze oder Speicherbeschädigungen).
Da jeder TreeNode seinen linken und rechten Knoten besitzt, ist es wahrscheinlich das Vernünftigste, Kopien zu erstellen. Du würdest also etwas ähnliches schreiben:
%Vor%Muss ich denselben Code (außer return) sowohl für den Copy-Konstruktor als auch für operator =?
implementieren?
BEARBEITEN - Der obige Absatz sollte lauten: Der Code in jedem sollte das gleiche Endergebnis haben, da die Daten von dem anderen Objekt kopiert werden. Dies beinhaltet oft sehr ähnlichen Code. Der Zuweisungsoperator muss jedoch wahrscheinlich überprüfen, ob bereits etwas zu left
und right
zugewiesen wurde, und diese somit bereinigen. Als Konsequenz muss es möglicherweise auch auf die Selbstaufgabe achten oder auf eine Weise geschrieben werden, die während der Selbstaufgabe nichts Schlimmes verursacht.
Tatsächlich gibt es Möglichkeiten, einen mit dem anderen zu implementieren, so dass der eigentliche Code, der die Elementvariablen manipuliert, nur an einer Stelle geschrieben wird. Andere Fragen zu SO haben das diskutiert, wie zum Beispiel dieser .
So würde ich es machen:
Da Sie zwei Ressourcen im selben Objekt verwalten, wird dies etwas komplexer (weshalb ich empfehle, niemals mehr als eine Ressource in einem Objekt zu verwalten ). Wenn Sie das Copy / Swap-Idiom verwenden, beschränkt sich die Komplexität auf den Copy-Konstruktor (was für die starke Ausnahmegarantie nicht zu trivial ist).
Der schwierige Teil jetzt:
%Vor%Ist dies der richtige Weg, den Standardkonstruktor zu implementieren?
Nein, Aufruf von delete
für etwas, das nicht mit new
zugeordnet wurde, ruft Undefined Behavior auf (in den meisten Fällen führt dies zum Absturz Ihrer Anwendung)
Setzen Sie Ihre Zeiger auf NULL
in Ihrem Standardkonstruktor.
Ich sehe solche Probleme nicht mit dem Rest Ihres Codes. Ihr Zuweisungsoperator seicht die Knoten, während Ihr Kopierkonstruktor sie tief kopiert.
Befolgen Sie einen geeigneten Ansatz gemäß Ihrer Anforderung. : -)
BEARBEITEN :
Anstatt Zeiger in Ihrem Standardkonstruktor zuzuweisen, verwenden Sie eine Initialisierungsliste
Tags und Links c++ rule-of-three