C ++ Konstruktor + Zeigerobjekt kopieren

8

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

  1. Ist dies der richtige Weg, den Standardkonstruktor zu implementieren? Ich bin mir nicht sicher, ob ich es haben muss oder nicht. Aber was ich in einem anderen Thread über den Kopierkonstruktor mit Zeiger gefunden habe, ist, dass ich den Platz für diesen Zeiger reservieren muss, bevor ich die Adresse im Kopierkonstruktor kopieren kann.
  2. Wie wird die Zeigervariable im Kopierkonstruktor zugewiesen? Die Art, wie ich in Copy Constructor geschrieben habe, könnte falsch sein.
  3. Muss ich denselben Code (außer return) für den Kopierkonstruktor und den Operator =?
  4. implementieren?
  5. Stimmt es, dass ich den Zeiger im Destruktor löschen muss?

    %Vor%

Implementierung

%Vor%

Vielen Dank im Voraus.

    
Michael Sync 18.09.2010, 05:07
quelle

5 Antworten

21
  

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?

Fast sicher. Oder zumindest sollte der Code in jedem das gleiche Endergebnis haben. Es wäre sehr verwirrend, wenn die Konstruktion und die Zuordnung der Kopien unterschiedliche Auswirkungen hätten.

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 .

    
TheUndeadFish 18.09.2010, 06:08
quelle
8

Noch besser, denke ich

%Vor%

Außerdem müssen Sie die Zeiger "links" und "rechts" in der Zuweisungsoperation löschen oder Sie haben ein Speicherleck

    
frag 18.09.2010 05:29
quelle
3

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).

%Vor%

Der schwierige Teil jetzt:

%Vor%     
Martin York 18.09.2010 16:23
quelle
1
  

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.

%Vor%

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

    
Prasoon Saurav 18.09.2010 05:14
quelle
1

Darf ich auch boost :: shared_ptr aus der Bibliothek Boost (wenn Sie es verwenden können) anstelle von einfachen Zeigern vorschlagen? Es wird viele Probleme lösen, die Sie möglicherweise mit ungültigen Zeigern haben, tiefe Kopien e.t.c.

    
frag 18.09.2010 06:11
quelle

Tags und Links