2016-05-23 9 views
0

seien folgende Klassendeklaration:Destruktor einer Klasse C mit Mitgliedern der Zeiger C

class NTree 
{ 
private: 
    const T* fKey; 
    NTree<T, N>* fNodes[N]; // N subtrees of degree N 
    NTree(); 
... 
} 

in denen wir einige Fnodes hinzufügen können, die eine Teilstruktur ein Index angegeben. Diese werden dynamisch unter Verwendung von new zugewiesen. Allerdings gibt es Elemente, die statisch sind, und nicht dynamisch zugewiesen:

public: 
    static NTree<T, N> NIL; // sentinel 
... 

Wir wählen diese oben mit dem mitgelieferten Standard-Konstruktor auf dem Stapel zuzuordnen.

template<class T, int N> 
NTree<T, N> NTree<T, N>::NIL; 

Nun wollen wir einen NTree löschen. Die Klasse NTree ist rekursiv und enthält einen Zeiger auf NTree. Das ist, was ich kämpfe.

Ich verstehe die Logik hinter einem Destruktor, wenn wir z.B.

Wir könnten einen Destruktor verwenden, um zu verhindern, dass diese Zeiger baumeln oder verloren gehen.

~MyClass() 
{ 
    delete myA; 
    delete myB; 
    delete myC; 
} 

Wenn es jedoch zu einer rekursiven Klasse kommt, ich habe keine Ahnung, wie meine Gedanken um diese zu wickeln, wie die Löschung zu verstehen.

Eine einfache Sache zu denken:

template<class T, int N> 
NTree<T, N>::~NTree() 
{ 
    delete[] fNodes; 
} 

Allerdings wird es nicht funktionieren, da einige Knoten sind NIL (Stack zugeordnet), so löschen sie in einem Absturz führen.

Eine weitere Idee ist:

template<class T, int N> 
NTree<T, N>::~NTree() 
{ 
    for (int i = 0; i < N; i++) 
    { 
     delete fNodes[i]; 
    } 
} 

Dies ist jedoch in einem Stapelüberlauf führen, weil der Stapel mit Frames für jeden rekursiven Aufruf von ~NTree()

und die folgenden bombardiert:

template<class T, int N> 
NTree<T, N>::~NTree() 
{ 
    for (int i = 0; i < N; i++) 
    { 
     if (fNodes[i] != &NIL) 
      delete fNodes[i]; 
    } 
} 

Ergebnisse in einer Leseausnahme, weil die rekursiven Aufrufe fNodes [i] für einen bestimmten Stapelrahmen freigeben und so versuchen, auf mich zuzugreifen mory ist ungültig.

Also meine Frage ist, wie kann ich eine Elementvariable löschen, wo dieses Mitglied rekursiv als die gleiche Klasse definiert ist?

Wie kann ich meinen Destruktor arbeiten lassen?

Edit: Versuch mehr Informationen zu versorgen, ohne dass es zu verworren machen

ich ein destructor bin definieren, so dass es wahrscheinlich klug, du meine Kopie zu zeigen, Konstruktor und Zuweisungsoperator:

template<class T, int N> 
NTree<T, N> & NTree<T, N>::operator=(const NTree & aOtherNTree) 
{ 
    //This is an already initialized object. 
    if (this != &aOtherNTree) 
    { 
     fKey = aOtherNTree.fKey; 

     for (int i = 0; i < N; i++) 
     { 
      if (fNodes[i] == &NIL) 
       continue; //continue if nil 

      delete fNodes[i]; //important -- so no dangling pointer 
      fNodes[i] = new NTree<T, N>; //allocate memory 
      fNodes[i] = aOtherNTree.fNodes[i]; //assign 
     } 
    } 

    return *this; 
} 

. .

Ich hoffe, dies zeigt alle Instanzen von, wenn ich Speicher zuweisen, die explizite Löschung im Destruktor benötigt. NIL ist ein Wächter, wir weisen NIL immer ein Blatt zu.

Dieser Teil des Professors zur Verfügung gestellt wird, ist es, wo wir die ersten Objekte einrichten:

NS3Tree root(A); 
root.attachNTree(0, *(new NS3Tree(A1))); 
root.attachNTree(1, *(new NS3Tree(A2))); 
root.attachNTree(2, *(new NS3Tree(A3))); 
root[0].attachNTree(0, *(new NS3Tree(AA1))); 
root[1].attachNTree(0, *(new NS3Tree(AB1))); 
root[1].attachNTree(1, *(new NS3Tree(AB2))); 

A1, A2, usw. sind sind Strings

+1

'delete [] fNodes' 'fNodes' wurde nicht mit' new' versehen, so dass Sie kein Geschäft haben, es zu löschen. Vielleicht möchten Sie einzelne Elemente des Arrays löschen, vorausgesetzt, dass * diese * mit 'neu' erhalten wurden. –

+0

@IgorTandetnik Ah, ja. Du hast recht. das Löschen von fNodes würde in diesem Fall definitiv nicht funktionieren. Ich zeige jedoch zwei Methoden, in denen ich versuche, auch die Elemente von fNodes zu löschen, aber ohne Erfolg. – Kevin

+0

Durchqueren Sie zuerst die Baumtiefe (unter Verwendung eines expliziten Stapels, nicht rekursiver Aufrufe). Löschen Sie die Knoten von unten nach oben. –

Antwort

1

Ihre Copykonstruktor und Zuweisungsoperator total beide falsch.

 if (fNodes[i] == &NIL) 
      continue; //continue if nil 
     delete fNodes[i]; //important -- so no dangling pointer 

Dies ist eine falsche Logik. Wenn Ihr alter Kindwert NIL war, bleibt es NIL für immer, weil es nie zugewiesen wird. Dies sollte sein:

 if (fNodes[i] != &NIL) 
      delete fnodes[i]; 

Natürlich in der Kopie Ctor dem obigen Fragment sollte nicht angezeigt werden, weil Fnodes [i] keinen bestimmten Wert haben. Es sollte nur in der Zuordnung erscheinen.

Jetzt

 fNodes[i] = new NTree<T, N>; //allocate memory 
     fNodes[i] = aOtherNTree.fNodes[i]; //assign 

Sie einige Knoten zuweisen und dann sofort einen Zeiger auf sie mit einem anderen Zeiger überschrieben werden soll, durch einen anderen Knoten verwaltet. Die erste Zuweisung hat somit keine Auswirkung, außer für ein Speicherleck. Der zweite Fehler führt später zu einem Fehler. Hier ist ein korrekter Aufruf

 if (aOtherNTree.fNodes[i] == &NIL) 
      fNodes[i] = &NIL; 
     else 
      fNodes[i] = new NTree<T, N> (*aOtherNTree.fNodes[i]); // make a new copy 

Eine alternative else-Klausel ist

 else { 
      fNodes[i] = new NTree<T, N>; 
      *fNodes[i] = *aOtherNTree.fNodes[i]); // assign the object, not the pointer 
     } 

Ich empfehle, eine Debug-Funktion zu schreiben, die einen Baum, einschließlich der Adresse jeden Knotens drucken. Während des Debuggens drucken Sie jeden Baum, den Sie erstellen, um sicherzustellen, dass keine Pointer-Freigabe erfolgt.

+0

Das behebt es, danke. Wird akzeptieren. Ich denke, dass der Übergang von einer verwalteten Sprache zu einer nicht verwalteten Sprache ihre Schwierigkeiten hat. Wie auch immer, deine erste Aussage über "Das ist falsche Logik", ich sehe nicht, wie die beiden anders sind, schau dir bit.ly/1OSwg4E an - in der ersten Instanz, meinem Code, überprüfe ich, ob der Knoten gleich null ist Wenn ja, dann fortfahren, werden alle Anweisungen unter dem Fortfahren nicht ausgeführt, daher gibt es keinen funktionalen Unterschied zwischen dem Prüfen auf Null und Fortsetzen, dann wenn es nicht wahr ist, weisen wir es dem Nicht-Nicht-Prüfen zu, und wenn Wahr zuordnen. Können Sie das bitte erklären? – Kevin

+0

Ich weiß, dass ich offensichtlich falsch liege, aber ich würde gerne wissen, warum ich falsch liege. – Kevin

+0

@Kevin das ist nicht der gleiche Code. Ihr realer Code hat Anweisungen im Schleifenkörper nach dem Fortsetzen. Hier liegt der Unterschied. Weiter wird dazu führen, dass sie übersprungen werden. Sie wollen sie nie überspringen. –