C++ 链表、树等的递归析构函数坏吗?

C++ 链表、树等的递归析构函数坏吗?,c++,coding-style,recursion,destructor,C++,Coding Style,Recursion,Destructor,在我目前的学习练习中,我正在学习链表和树。我最近看到一个建议,通过让每个节点删除其子节点来递归地破坏数据结构。然而,在我发现的几乎所有示例中,节点析构函数都是空的,一些管理类使用某种形式的迭代和删除来处理销毁。从可靠性和/或风格的角度来看,递归析构函数有什么固有的缺点吗 下面是我对这两种方法理解的实现 递归销毁: #include <iostream> struct Node { static int count; Node() : num_(count++), p_next

在我目前的学习练习中,我正在学习链表和树。我最近看到一个建议,通过让每个节点删除其子节点来递归地破坏数据结构。然而,在我发现的几乎所有示例中,节点析构函数都是空的,一些管理类使用某种形式的迭代和删除来处理销毁。从可靠性和/或风格的角度来看,递归析构函数有什么固有的缺点吗

下面是我对这两种方法理解的实现

递归销毁:

#include <iostream>
struct Node {
  static int count;
  Node() : num_(count++), p_next_(0) {}
  ~Node() { 
    std::cout << "entering " << num_ << "\n";
    delete p_next_;
    std::cout << " leaving " << num_ << "\n";
  }
  const int num_;
  Node* p_next_;
};
int Node::count = 0;
int main () {
  Node* p_head = new Node();
  p_head->p_next_ = new Node();
  p_head->p_next_->p_next_ = new Node();
  delete p_head;
  return 0;
}
假设我正确地读取了结果,我的两个实现做了相同的事情

关于我的递归销毁示例,我认为在解决代码的实际问题时,我几乎总是需要某种管理类来保存head的副本,但是管理类只需要删除head/根节点,以确保销毁整个数据结构。对我来说,这感觉更优雅,但我已经陷入了我认为整洁的代码的麻烦中

管理类是否应该负责确保所有内容都被正确删除?还是让底层数据结构知道如何清理自己更好?有没有不明显的陷阱

谢谢

--约旦

编辑:我想到了一个主意。如果我有一个异常长的节点链,那么在第一个示例中,由于递归在起作用,我是否需要担心在销毁过程中堆栈溢出


edit2:我想这应该是显而易见的。现在我只是觉得有点傻。在我的机器上,如果我有超过64910个节点,我就会崩溃。所以递归显然是一个陷阱。

想想链接项的所有权

节点对象拥有它的近亲有意义吗? 一个节点拥有附加的卫星数据肯定是有意义的,所以当该节点被破坏时,您应该清理这些数据

LinkedList拥有自己的节点,因此它应该负责在没有任何内存剩余的情况下正确销毁它们

LinkedList对象应该能够添加和删除节点,因此从所有权的角度来看,它负责清理节点,例如删除列表中的每个节点
反复地

如果对链接列表执行此操作,将出现堆栈内存消耗问题。销毁这样一个链表将导致
节点的递归d'tor
,而递归深度将随着链表的大小线性增长

只需做一个实验:在列表中输入数百万个节点,然后销毁它。我打赌你会得到堆栈溢出(除非你将线程配置为保留巨大的堆栈大小)。特别是在调试构建中,您将很早就用完堆栈

至少从技术角度来看,OTOH对树这样做是可以的。树清理通常以递归方式实现,即使上述递归函数属于树或
节点
也不重要


销毁树的递归深度将随树深度对数增长,这是正常的。

那么您的意思是说b/c管理类拥有由链接节点形成的底层数据结构,将清理功能提供给节点是令人困惑的/不好的形式?通常不是这样,但对于此声明适用的节点而言。请这样看:LinkedList拥有节点,因此LinkedList负责在使用delete销毁列表时释放节点,并调用析构函数。对于同一规则适用的节点,节点负责销毁其卫星数据,但不销毁其链接到的其他节点,因为这些节点(下一个和上一个)不属于节点,而是属于LinkedListYes。反对第一种方法的主要论点是,包含多个节点的列表确实可能导致堆栈溢出,即使一个好的编译器应该能够发现每次调用都不需要额外的堆栈空间,因为没有返回的结果可供处理。但我认为依赖一个好的编译器是不安全的?你又是对的。有些语言中递归是重复的主要工具,并且可以保证类似的方法不会浪费资源。然而,C++Ok的情况并非如此,那么使用递归销毁是否有任何理由呢?我原以为清理树(假设节点有p_left_和p_right_)应该容易得多,但现在我想知道递归是否应该移动到管理类中的一个方法中(基于Wizz的点)。树的不同之处在于,如果它是合理平衡的,它的高度是节点数的对数,也就是说,所需的堆栈数量是有限的。此外,由于树本身是递归的,因此另一种方法是保留所遍历节点的某种堆栈,这种方法的复杂性使得递归更具吸引力。并非每棵树都是平衡的。
~Node() {std::cout << "Someone deleted " << num_ << "\n";}
/* 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;
}