【问题标题】:Correct way of erasing a linked list删除链表的正确方法
【发布时间】:2011-08-30 06:54:58
【问题描述】:

假设,我有一个单链表,它的基本构建块是,

struct Node {
  Data d;
  Node *pNext;
  // methods
  ~Node();
};

链表的头部存储为,

Node *m_Head; // member of some class

当我完成列表时,我将通过删除每个节点来清理它,

void Erase()
{
  Node *pIter, *pTemp = m_Head;
  while((pIter = pTemp) != 0)
  {
    pTemp = pIter->pNext;
    delete pIter;
    pIter = pTemp;
  }
}

我想,如果我可以简化这个。所以我想出了一个想法,只需一条指令就可以清理整个链表!

delete m_Head;

析构函数看起来像:

Node::~Node() { delete this->pNext; }

我担心的是,它会导致递归(隐含由于delete)吗?如果是,那么对于更大的链表来说绝对是一个问题。编译器能以任何方式帮助优化吗?

[注意:不使用任何库设施,如 std::list 或其他。]

【问题讨论】:

  • 注意,如果选择了后面的【使用删除】,在删除前要设置node->pNext = NULL;【修改列表时,不要销毁】,否则会失去这个的尾巴子列表。

标签: c++ recursion linked-list memory-management


【解决方案1】:

我想你要问的问题是,列表中的每个Node拥有它的pNextNode吗?如果不是,那么它就没有必要在其析构函数中删除其pNext 节点。

在大多数链表实现中,所有节点都归链表所有,一个节点并不拥有链表中它之后的所有节点。将节点保持为哑(POD 结构)并让所有逻辑驻留在列表中更有意义。

您的节点具有析构函数但没有复制构造函数或复制赋值运算符,这绝对是一种设计“气味”。我认为当您编写实现插入、拼接和擦除单个元素功能的代码时,这种方法会导致更多复杂性,因为无论如何您都必须手动管理pNext 指针,以避免无意破坏列表的整个尾部。

【讨论】:

  • cons 列表中,一个节点拥有它之后的那些似乎很自然,因为定义是List a = Empty | Cons a List。我同意这需要小心操作。
【解决方案2】:

当然:仅出于学习目的或确定自己的 List 确实更适合您的用例时才这样做


这取决于。您的编译器可能会检测到tail recursion 并发出在概念上等同于使用循环的代码。

如果不是,那么是的,它将递归。通常,如果堆栈压力很小(如您的情况),商品盒上应该可以进行数千次递归。然而,这并不能保证,事实上,对于非常大的列表,这可能是一个问题。

另外,我认为递归确实不完全适合兄弟节点的概念。一个节点层次结构,比如quadtrees,呼吁递归,但是当列表概念是关于兄弟时,我不太擅长考虑递归(形成一个调用层次结构-节点。

您也可以将手动循环视为对递归的一种易于实现的优化,它可以保证您的代码更加健壮。

顺便说一句,您也可以 应该将删除的节点删除到持有者类中:

class List {
public:
    ~List() {
        for-each-node
            delete-node
    }

private:
    class Node {
        Node *node_;
        ...
    };
    ...
};

这基本上是标准库的列表通常是如何实现的。它使整个实现更容易实现并且在概念上更正确(节点在逻辑上不拥有它们的兄弟姐妹)

【讨论】:

  • AFAIK,Data 成员(和Node 的基类)的破坏发生在Node 析构函数调用的末尾,它使它根本不是尾递归的(除非所有这些都是无操作和优化的)。
【解决方案3】:

大多数编译器在默认设置中执行tail call elimination。一些更聪明的人可以将非尾调用转换为尾调用。

所以,只要你开启了一些优化,这个方法就可以了。

【讨论】:

    【解决方案4】:

    原始指针?手动调用delete ?

    为什么不,简单地说:

    struct Node {
      Data d;
      std::unique_ptr<Node> next;
    };
    

    那你根本不用担心内存管理,它是自动的!

    【讨论】:

    • 实际上,这会产生递归,但这会阻止stackoverflow吗?这是问题的主要关注点。
    • @stefaanv:开放主题。这取决于编译器和标准库的实现,我希望是这样,但这是猜测。我不知道标准是否承诺最小递归深度,如果它不关心这一点,我不会感到惊讶。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-19
    • 2015-07-18
    • 2017-07-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多