2017-01-19 3 views
0

Учитывая это:с ++ удаление всех детей в структуре дерева, информируя родителей

class A {  
public: 
    A() 
    ~A() 
    void add(A *child); 
    void remove(A *child); 
    void set_parent(A *parent); 
private: 
    A *parent; 
    std::list<A*> children; 
}; 

add, remove и set_parent делают очевидным доступа списка или родительский атрибут.

Со следующим реализации:

A::A() : parent(nullptr){}; 
A::~A() { 
    //Keep the hierarchy clean and inform parent that we are gone now 
    if(this->parent != nullptr) { 
     this->parent->remove(this); 
    } 
    //Also delete all children as they would be lost now ... 
    for(A *child : children) { 
     delete child; 
    } 
} 
void A::set_parent(A *parent) 
{ 
    if(this->parent != nullptr) 
     this->parent->remove(this); 
    this->parent = parent; 
} 

На самом деле я не хочу делать что-нибудь более сложное, но проблема будет, что при удалении детей списка каждый из них информировать своих родителей, что они ушли в настоящее время, манипулируя списком, который мы сейчас итерируем. Что можно сделать?

ответ

2

Как насчет «отсоединения» родителя перед удалением ребенка?

A::~A() { 
    //Keep the hierarchy clean and inform parent that we are gone now 
    if(this->parent != nullptr) { 
     this->parent->remove(this); 
    } 
    //Also delete all children as they would be lost now ... 
    for(A *child : children) { 
     child->set_parent(nullptr); // Detach parent to avoid re-calling remove() 
     delete child; 
    } 
} 
+0

У меня есть не добавил это к деталям, но прямо сейчас set_parent также информирует текущего родителя (если есть) о том, что ребенок изменил его владельца – salbeira

+1

Вот почему вы должны поместить всю минимальную релевантную информацию в свой вопрос, вы можете получить как ub-оптимальный ответ. Во всяком случае, он может по-прежнему работать, если у вас есть способ «отсоединить» это уведомление. В конце концов, вы уничтожаете ребенка, нет никакой полезной информации в рассказе родителям о том, что ребенок меняет родителя (на nullptr, потому что он отделяет его). – roalz

+0

Прошу прощения, я исправил его и для вашей «идеи» я добавил «если new_parent! = Nullptr» в условие set_parent – salbeira

0

Вы можете избежать этой проблемы, не итерацию над list в вашем деструкторе. Вместо этого удалите детей, пока есть дети.

A::~A() { 
    //Keep the hierarchy clean and inform parent that we are gone now 
    if(this->parent != nullptr) { 
     this->parent->remove(this); 
    } 
    //Also delete all children as they would be lost now ... 
    while(children.empty() == false) 
    { 
     delete children.back(); 
    } 
} 
1

Самого простое и прямолинейное решение, которое я могу думать, чтобы просто иметь логический флаг, что, когда множество причины remove не просто ничего не делать. Установите этот флаг перед итерацией по списку.

Тогда у нас есть решение, которое на самом деле может быть лучше: умные указатели. Тогда вам вообще не нужен цикл в деструкторе, когда сам объект списка уничтожается, содержащиеся указатели просто «удаляют» самих себя. std::shared_ptr и std::enable_shared_from_this - две хорошие ссылки, которые вы можете изучить для этого.

Тогда лучшее решение, ИМО: Отделить удаление элементов от деструктора. Имейте явную функцию, которая удаляет элемент из родителя и не имеет вызова remove в деструкторе. Это, в сочетании с общими указателями, вероятно, самый безопасный способ справиться с этим. Могут потребоваться некоторые изменения (и, возможно, некоторые рефакторинг) с вашей стороны.

1

С std::list легко снять и перебрать, потому что итератор становится недействительным только тогда, когда соответствующий элемент удаляется:

auto child = std::begin(children); 

while(child != std::end(children)) 
{ 
    auto next = child + 1; 

    delete *child; 

    child = next; 
} 
0

Вы также можете сделать что-то вроде этого:

for (auto it = children.begin(); it != children.end();) 
{ 
    (*it)->set_parent(nullptr) 
    delete *it; 
    it = children.erase(it); 
} 
Смежные вопросы