【问题标题】:Writing a LinkedList destructor?编写 LinkedList 析构函数?
【发布时间】:2025-11-28 16:30:01
【问题描述】:

这是一个有效的 LinkedList 析构函数吗?我仍然对他们感到困惑。

我想确保我理解正确。

 LinkedList::~LinkedList()
 {
   ListNode *ptr;

   for (ptr = head; head; ptr = head)
   {
     head = head->next
     delete ptr;
   }
}

所以在循环开始时,指针 ptr 被设置为保存头的地址,即链表中的第一个节点。然后将 head 设置为下一项,一旦第一次删除发生,它将成为列表的开头。 ptr 被删除,第一个节点也是如此。在循环的第一次迭代中,指针再次设置为 head。

我担心的是到达最后一个节点。条件“头”;应该检查它是否不为空,但我不确定它是否会起作用。

任何帮助表示赞赏。

【问题讨论】:

  • 你为什么不尝试通过调试器运行代码,看看它是否有效?
  • @Manuel,因为某些平台上的调试器没有集成,很难使用?
  • 我知道我会为此被枪毙(总有人这样做,但我是个信徒)。 head 是一个成员变量,你真的应该有成员变量的命名约定,比如 m_head 或 head_

标签: c++ linked-list destructor


【解决方案1】:

为什么不简单得多——使用优雅的while-loop,而不是尝试仔细分析过度复杂的for-loop 是否正确?

ListNode* current = head;
while( current != 0 ) {
    ListNode* next = current->next;
    delete current;
    current = next;
}
head = 0;

【讨论】:

  • 当然可以..但是OP提到的原始代码是否可以工作?
  • 我应该说current 永远不会改变,next 永远不会被使用。这是否意味着对 OP 的一个坏双关语?
  • @Matthieu M.:没有不好的双关语。请您详细说明“从不改变”和“从不使用”?
【解决方案2】:

条件“头;”应该检查它是否不为空,但我不确定它是否会起作用。

是的,“head”本身与“head != null”是一样的——但即使你觉得它令人困惑,为什么还要使用无意义的打字快捷方式呢?只需多敲 6 次键(并生成相同的机器代码),所以请选择长格式。

此外,由于您使用的是for() 构造,因此您的代码比必要的要复杂一些。为什么不使用while()?你的代码会更干净。

最后,我意识到您这样做是为了学习,但请记住 list 在标准库中 --- 链接列表是官方的“已解决问题”。

【讨论】:

  • 我的意思是,鉴于 head 可能为空,我会在访问“next”时遇到问题吗?据我了解, head 将指向最后一个节点,访问 next 将包含 null 而不是下一个节点的地址。
【解决方案3】:

你可以通过调试器运行它,或者你可以通过你头骨内的那个湿件运行它——两者都会告诉你它工作正常。例如,让我们从列表开始:

head(47) -> [47]single_node -> [NULL]end-of-list.

通过您的陈述运行该列表:

  • ptr = headptr 设置为 47。
  • head 非零,因此进入循环。
  • head = head->nexthead 设置为 NULL。
  • delete ptr 将删除 single_node
  • ptr = headptr 设置为 NULL。
  • head 现在是 NULL (0) 所以退出循环。

你去吧,你已经删除了列表中的唯一条目,head 现在设置为 NULL。这就是您需要做的所有事情。

你可以用一个更长的列表或一个空列表做类似的事情,你会发现它仍然没问题(一个元素的列表和一个五十个元素的列表没有真正的区别)。

顺便说一句,我不太喜欢将指针视为布尔值 - 我宁愿把它写成这样:

for (ptr = head; head != NULL; ptr = head)

在我看来,它使代码可读性更好,而且你并没有真正牺牲任何性能(除非你有一个无脑编译器)。但这是一个品味问题。

你的评论:

我担心的是到达最后一个节点。条件“头”;应该检查它是否不为空,但我不确定它是否会起作用。

它会起作用的。零值将被视为假,因此您会发现当 head 为 NULL 时您永远不会取消引用 head->next 仅仅是因为您将在该点之前退出循环体(或者如果列表为空,甚至没有进入循环体)。

任何其他指针值都将被视为真,您将进入或继续循环体。

【讨论】:

    【解决方案4】:

    您的代码可能是正确的,您应该尝试运行它,例如valgrind 看看它说了什么。但是,我会这样写:

    for (ListNode *current = head, *next; current; current = next) {
        next = current->next;
        free(current);
    }
    

    【讨论】:

      【解决方案5】:

      测试正常

      类 List 的析构函数

      List::~List()
      {
          Node *n = this->front, *current = NULL; //initialization part
      
          while(n)                               //start cleanup of nodes of the list
          {
              current = n;
              n=n->next;
              delete(current);
          }
      
          front = end = NULL;
      }
      

      【讨论】:

        【解决方案6】:

        这是一种使用链表的析构函数释放/删除内存的更好方法。

        List()::~List()
                    {
                        for( Link* ptr= Head; Head; Head= Head->next)
                        {
                            delete ptr;
                        }
                    }
        

        【讨论】: