C++ 删除树结构中的所有子项,同时通知父项

c++ deleting all children in a tree structure while informing the parent

本文关键字:通知 删除 结构 C++      更新时间:2023-10-16

考虑到这一点:

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, removeset_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;
}

我实际上不想做任何更复杂的事情,但问题是,当删除列表的子级时,他们每个人都通知他们的父级他们现在已经消失了,操纵我们目前正在迭代的列表。一个人能做什么?

在删除子项之前"分离"父项怎么样?

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;
    }
}

我能想到的最简单和最直接的解决方案是简单地有一个布尔标志,当设置时会导致remove什么都不做。在循环访问列表之前设置此标志。

然后我们有一个实际上可能更好的解决方案:智能指针。然后你根本不需要析构函数中的循环,当列表对象本身被破坏时,包含的指针将简单地"删除"自己。 std::shared_ptrstd::enable_shared_from_this是您可能想要为此学习的两个很好的参考。

然后是最好的解决方案,IMO:将元素的移除与析构函数分离。有一个显式函数,该函数从父元素中删除元素,并且在析构函数中没有 remove 调用。这与共享指针相结合,可能是处理它最安全的方法。不过,您可能需要进行一些重新设计(可能还需要一些重构)。

使用 std::list 很容易删除和迭代它,因为迭代器仅在删除相应元素时失效:

auto child = std::begin(children);
while(child != std::end(children))
{
   auto next = child + 1;
   delete *child;
   child = next;
}
可以通过

不循环访问析构函数中的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();
    }
}

你也可以做这样的事情:

for (auto it = children.begin(); it != children.end();)
{
   (*it)->set_parent(nullptr)
   delete *it;
   it = children.erase(it);
}