双链列表无限循环?

时间:2011-09-25 05:50:42

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

如果我要创建一个节点类,如下所示,如果它在双向链表中使用,它会在解构双链表时创建一个无限循环吗?还是会很好地终止?

class Node
{
    Node(  );

    ~Node(  )
    {
       delete mNext; //deallocs next node
    }

    Contact mContact;
    Node* mPrevious;
    Node* mNext;
}; 

编辑:如果我将代码修改为此可行吗?

~Node(  )
{
   mPrevious = NULL;
   if (mNext->mPrevious != NULL)
   {
      delete mNext; //deallocs next node
   }
}

编辑2:或者这会最好吗?

~Node(  )
{
   if (mPrevious != NULL)
   {
      mPrevious = NULL;
      delete mNext; //deallocs next node
   }
}

5 个答案:

答案 0 :(得分:2)

如果考虑mNext指针,节点正在形成一个循环,那么任何节点的破坏确实可能形成一个无限的递归循环,它将通过炸毁来终止程序堆栈。

可能发生的事情是

  1. 发出第一个“外部”delete node;
  2. 当进入节点析构函数时,还没有做任何事情,因为代码析构函数是在破坏过程中执行的“第一”事情(破坏过程本身非常复杂,包括析构函数代码,类更改,成员破坏,基本破坏)此订单:有关更详细的说明,请参阅this answer
  3. 第一个析构函数指令填充执行delete mNext;,从而在循环中的下一个节点上触发相同的进程。
  4. 因为节点正在形成一个循环,所以这个链将从后面再次到达node,从而使第一次调用成为一个永远不会结束的递归。
  5. 每次调用都会为激活记录分配堆栈空间,因此一段时间后,允许用于堆栈的所有内存都将耗尽,操作系统将终止进程。删除调用不是“尾调用”,因为在析构函数代码完成后,必须释放内存,因此不能轻易地优化此递归...而delete mNext;是析构函数上的最后一个语句仍有操作必须在delete运算符完成后执行。
  6. 但请注意,根据我的经验,除非您使用特殊的编译器选项,否则不会检查堆栈溢出,因此程序终止将非常“异常”。另请注意,在Windows下有一些可怕的代码,如果它们在程序终止时发生,在某些情况下会隐藏段错误,因此很可能在退出事件循环后,Windows程序显然可以在此操作中正常终止。

    赋予堆栈overlflow通常不会被认为确实存在任何行为,包括明显的“无限循环”(请注意,这个无限循环可能不是递归析构函数之一,但在运行时系统内某处因为堆栈溢出)。

    为什么我使用这个词?原因是C ++标准说对象的多次破坏是Undefined Behavior。如果你在C ++中没有办法在没有完成销毁的情况下退出析构函数这一事实,你会理解理论上允许编译器将对象标记为“被销毁”并使守护进程飞出你的如果您输入两次相同对象的析构函数,则为nosrils。然而,检查此错误并不是必需的,并且编译器编写器通常是惰性的(这不是程序员的侮辱),因此不太可能存在此检查(除非启用了一些特殊的额外调试选项)。

    总结一下:它能永远循环吗?是。可以崩溃吗?当然。它能不能告诉我一个物体被摧毁了两次?当然。它可以很好地终止程序(即设置任何错误代码)?是的,那也是。

    任何事情都可能发生。墨菲说会发生任何对你造成最大伤害的事情......例如,在你开发它的过程中,程序会每次都很好地终止......而且在演示日期间它会在你脸上严重崩溃一千名潜在客户面前。

    请不要这样做: - )

答案 1 :(得分:1)

没有办法知道何时停止,所以它可能无限运行 你应该编写一个List类,它有一个指向(或实际)Node的指针。 Node's d'tor应该只关注自己的字段,在这种情况下mContactList's d'tor应迭代列表中的所有节点(记住何时停止),并删除每个节点(只需一次)。

答案 2 :(得分:1)

假设您将mNext初始化为null,它将无法无限运行。删除在遇到空指针时不会执行任何操作。因此它会在你期望的时候结束。

我不确定你用“if previous”选项做了什么。那些不行。这将是一个有效的节点,因此具有前一个节点或它将不是一个有效的节点,并且检查先前将有未定义的结果。坚持简单的回答:

class Node 
{ 
Node(  mNext = NULL; ); 

~Node(  ) 
{ 
   delete mNext; //deallocs next node 
} 

Contact mContact; 
Node* mPrevious; 
Node* mNext; 
};  

澄清:此解决方案有效,但仅在满足两个条件时: 1)列表中没有节点出现两次。 2)该清单不是循环的。 如果您能保证这些条件,这是您最简单的答案。如果你做不到,你需要做一些更复杂的事情。

答案 3 :(得分:0)

就我个人而言,我认为Node的析构函数应该与其他节点有关,这有点奇怪。

如果设计取决于我,我会创建一个List类,其中包含指向Node个对象(firstlast)的指针。 List类的析构函数将负责迭代列表中的所有节点并销毁它们。

答案 4 :(得分:0)

这实际上很简单。 假设 1)它是一个双重链接列表而不是循环链接列表 2)链接列表中没有循环:这是双链接列表 3)实现类只有一个Node实例,可能叫做HeadNode或LinkList;)这是明确销毁的节点


示例:LinkList是1-> 2-> 3-> 4-> 5-> 6-> NULL 对HeadNode的析构函数调用(reffer 3rd assumption)将导致一个递归调用,如下所示: 删除(1) - >删除(2) - >删除(3) - >删除(4) - >删除(5) - >删除(6) - > NULL 所以请chech if(mNext!= NULL)删除mNext 它的工作原理:)


但是:如果你想特别删除一个节点:假设我们只想在上面的例子中删除4个,那么所有的节点都会被删除到NULL,所以在删除之前请确保你将Mnext设置为NULL。


最佳做法是使用STL库或以其他方式使用autopointer类来解决问题的破坏部分