2017-08-23 15 views
1

J'ai un système de fenêtre dans une interface graphique, et je ne suis pas sûr de la séquence de destruction. Chaque fenêtre est un vecteur tenant ses enfants, et chacun a aussi un pointeur vers son parent:Meilleure façon de supprimer la hiérarchie des fenêtres parent/enfant

auto root = new Window; 
root->addChild(new Window); 
root->addChild(new Window); 
auto child = root->addChild(new Window); // Return value is the newly created Window 
child->addChild(new Window); 
child->addChild(new Window); 
auto grandchild = child->addChild(new Window); 
grandchild->addChild(new Window); 
grandchild->addChild(new Window); 
grandchild->addChild(new Window); 

// Je veux supprimer le pointeur d'enfant, d'abord je dois effacer le pointeur de son enfant de parent de vecteur .

child->parent->children.erase(child->parent->children.begin() + child->positionInParentsVector - 1); 

child->destroy(); 

void Window::destroy() 
{ 
    if (children.size() == 0) delete this; 
    else for (auto i : children) i->destroy(); 
} 

OU avec des vecteurs de pointeurs intelligents, serait-il suffisant pour faire:

// Remove reference of child from parent's children vector, then 

delete child; 

J'ai lu qu'il est correct de faire supprimer ce. C'est difficile de comprendre.

+0

pointeur intelligent serait probablement mieux il est tout nettoyé automatiquement. Cependant, si votre hiérarchie est * VERY * profonde, vous risquez d'avoir un débordement de pile dans les destructeurs. Dans ce cas, vous devrez tout libérer "manuellement" comme vous l'avez montré. –

+0

Aussi, parce que la fonction destroy est récursive, et j'ai seulement besoin de supprimer la référence du parent une fois au plus haut niveau, cela signifie que je dois faire une fonction pour supprimer la référence, et une autre pour faire les suppressions récursives, non? Ce serait probablement trois fonctions, une removeReferenceAndDestroy, et dans celui-là removeReference, et ensuite le récursif destroy(). Il n'y a pas de manière plus élégante? – Zebrafish

+0

Si le débordement de pile * est * un problème réaliste (cela se produira aussi pour une traversée récursive de la hiérarchie régulière) alors c'est en fait * pas * comme vous l'avez montré, vous devez le faire sans récursion mais avec des boucles. –

Répondre

1

Je pense que vous vous confondez un peu. L'exemple suivant autonome n'a pas de fuites de mémoire:

#include <utility> 
#include <vector> 
#include <memory> 

using std::vector; 
using std::unique_ptr; 

struct Window { 
    Window() = default; 
    vector<unique_ptr<Window>> children; 
    Window* parent = nullptr; 
    ~Window(); 

    Window* addChild(std::unique_ptr<Window> c) { 
     c->parent = this; 
     children.push_back(std::move(c)); 
     return children.back().get(); 
    } 
}; 

Window::~Window() = default; 

int main() { 
    auto root = std::make_unique<Window>(); 
    root->addChild(std::make_unique<Window>()); 
    auto child = root->addChild(std::make_unique<Window>()); 
    child->addChild(std::make_unique<Window>()); 
}; 

Une classe ne peut lui-même contenir, mais il peut contenir un unique_ptr à lui-même (ou des conteneurs de celui-ci). Dans ce schéma, toute la destruction récursive se produit correctement et automatiquement. Vous pouvez l'habiller de différentes manières bien sûr, mais c'est l'idée de base.

Il y a quelques autres questions ici de bonne conception d'objet et de classe et d'encapsulation, mais c'est un autre problème. Note: vous ne devriez pas avoir une méthode appelée destroy dans la grande majorité des cas, mais cela devrait plutôt se produire dans le destructeur.

Il ne devrait en principe jamais être nécessaire d'écrire nouveau ou supprimer en C++ 11 ou plus, sauf si vous écrivez du code de niveau relativement bas (comme votre propre allocateur de mémoire).

Un exemple concret: http://coliru.stacked-crooked.com/a/adef8cc40de4916a

+0

Merci. C'est intéressant comment la plupart des gens ont tendance à rester loin des pointeurs bruts, et comment va la tendance de C++. Je suppose que c'est une bonne chose pour le nettoyage automatique, certainement beaucoup plus facile. Un vecteur de pointeurs intelligents est toujours un appel supplémentaire, non? En détruisant le vecteur, le destructeur de chaque unique_pointer est appelé, qui appelle ensuite delete sur l'objet pointé. – Zebrafish

+0

@Zebrafish Vous devez faire attention. Les gens restent loin de * posséder * des pointeurs bruts. Mon code contient à la fois les variables membres du pointeur brut (généralement une mauvaise chose, mais parfois vous en avez besoin), et un pointeur renvoyé à partir d'une variable membre. Cependant, dans les deux cas, c'est une relation de non-possession.Notez que dans 'addChild', il vaut mieux vérifier que' c' possède réellement 'Window', et lancer si ce n'est pas le cas. Ensuite, vous pouvez retourner un 'Window &' qui est mieux car il ne peut pas être nul (je peux changer le code si vous êtes curieux). –

+0

@Zebrafish Un appel supplémentaire, par rapport à quoi? Votre description est correcte mais je ne vois pas comment cela pourrait être plus rapide si elle était codée avec un nettoyage manuel plutôt que automatique. –