Un destructeur récursif pour une liste chaînée, un arbre, etc. est-il mauvais?

Pour mon exercice d’apprentissage actuel, j’étudie des listes chaînées et des arbres. J’ai récemment vu une suggestion visant à détruire les structures de données de manière récursive en faisant en sorte que chaque nœud supprime son / ses enfants. Cependant, dans presque tous les exemples que j’ai trouvés, le destructeur de noeud est vide et une classe de gestion gère la destruction à l’aide d’une forme d’itération et de suppression. Du sharepoint vue de la fiabilité et / ou du style, y a-t-il quelque chose de fondamentalement mauvais dans le destructeur récursif?

Ce qui suit est ma mise en œuvre de ma compréhension des deux approches.

Destruction récursive:

#include  struct Node { static int count; Node() : num_(count++), p_next_(0) {} ~Node() { std::cout << "entering " << num_ << "\n"; delete p_next_; std::cout << " leaving " << num_ <p_next_ = new Node(); p_head->p_next_->p_next_ = new Node(); delete p_head; return 0; } 

Et voici mon sharepoint vue sur la destruction de la classe de gestion. En supposant que j’ai défini le DTOR suivant pour le nœud:

 ~Node() {std::cout << "Someone deleted " << num_ << "\n";} 

Je définirais la classe de gestion LinkedList suivante et la classe principale suivante

 /* other stuff from above */ class LinkedList { public: LinkedList() : p_head_(new Node()) { p_head_->p_next_ = new Node(); p_head_->p_next_->p_next_ = new Node(); } ~LinkedList() { while(Node* p_prev = p_head_) { p_head_ = p_head_->p_next_; delete p_prev; } } private: Node* p_head_; }; int main () { LinkedList* p_list = new LinkedList(); delete p_list; return 0; } 

En supposant que je lis correctement mes résultats, mes deux implémentations font la même chose.

En ce qui concerne mon exemple de destruction récursive, je pense que j’aurai presque toujours besoin d’une sorte de classe de gestion qui conserve une copie de head lorsque je résous un problème réel avec du code, mais la classe de gestion n’a besoin que de supprimer le nœud de tête / racine de afin d’assurer la destruction de l’ensemble de la structure de données. Cela me semble plus élégant, mais je me suis heurté à des problèmes de code que je pensais bien.

La classe de gestion doit-elle être responsable de s’assurer que tout est supprimé correctement? Ou est-il préférable que la structure de données sous-jacente sache se nettoyer elle-même? Y a-t-il des pièges qui ne sont pas évidents?

Merci!

–Jordan

edit: Une pensée m’est venue. Si j’ai une longue chaîne de nœuds, dois-je m’inquiéter d’un débordement de stack lors de la destruction dans le premier exemple, car la récursivité est en jeu?

edit2: Je suppose que cela aurait dû être évident. Et maintenant je me sens juste un peu bête. Sur ma machine, si j’ai plus de 64 910 nœuds, je tombe en panne. Donc, la récursivité présente clairement un piège.

Il y aura un problème de consommation de mémoire de stack si vous faites cela pour les listes chaînées. Détruire une telle liste liée causera une perte récursive de votre Node , alors que la profondeur de la récursivité augmentera linéairement avec la taille de votre liste liée.

Faites simplement l’expérience: entrez plusieurs millions de nœuds dans votre liste, puis détruisez-la. Je parie que vous aurez un débordement de stack (sauf si vous avez configuré votre thread pour réserver une taille de stack énorme). Surtout dans la construction de débogage, vous courrez très tôt hors de la stack.

OTOH le faire pour les arbres est acceptable, du moins du sharepoint vue technique. Le nettoyage de l’arbre est généralement implémenté de manière récursive de toute façon, même si la fonction récursive ci-dessus appartient à l’arbre ou au Node n’est pas importante.

La profondeur de récursion de la destruction de l’arbre augmentera logarithmiquement avec la profondeur de l’arbre, ce qui est correct.

Pensez aux propriétés de l’élément lié.

Cela a-t-il du sens qu’un object Node en soit le plus proche parent? Il est donc logique qu’un nœud possède les données satellite attachées. Vous devez donc le nettoyer lorsque le nœud est en cours de destruction.

La LinkedList est propriétaire de ses noeuds, il devrait donc être responsable de les détruire correctement sans aucun rest en mémoire.

L’object LinkedList doit être en mesure d’append et de supprimer des nœuds. Il est donc responsable, du sharepoint vue de la propriété, de les nettoyer, par exemple en supprimant chaque nœud de la liste de manière itérative.