双链表无限循环
Doubly-linked list infinite loop?
如果我要创建一个节点类,如下所示,如果它用于双链表,它会在解构双链表时创建一个无限循环吗?还是会很好地结束?
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
}
}
如果考虑到mNext
指针,节点正在形成一个循环,那么任何节点的破坏将确实可能形成一个无限递归循环,它将通过爆炸堆栈来终止程序。
可能发生的是
- 第一个"外部"
delete node;
发出 - 当进入节点析构函数时,还没有做任何事情,因为代码析构函数是销毁过程中执行的"第一件"事情(销毁过程本身非常复杂,按顺序包括析构函数代码、类更改、成员销毁、基销毁:参见此回答以获得更详细的解释)。
- 第一个析构指令填充执行
delete mNext;
,从而触发循环中下一个节点的相同进程。 - 因为节点正在形成一个循环,这个链将再次"从后面"到达
node
,从而使第一个调用成为一个永远不会结束的递归。 - 无论如何,每次调用都会为激活记录分配堆栈空间,因此一段时间后,允许用于堆栈的所有内存将被耗尽,操作系统将终止进程。删除调用不是一个"尾部调用",因为在析构函数代码完成后,内存必须被释放,所以这个递归不能轻易地被优化掉。虽然
delete mNext;
是析构函数上的最后一条语句,但在delete
操作符完成之后,仍然有一些操作必须执行。
考虑到堆栈溢出通常不被认为是任何可能的行为,包括一个明显的"无限循环"(注意,这个无限循环可能不是递归析构函数中的一个,而是运行时系统中的某个地方因为堆栈溢出而变得疯狂)。
为什么我用这个词可能是?原因是c++标准规定对象的多次销毁是未定义行为。如果您将此添加到c++中没有完成析构函数就无法退出析构函数的事实中,您将理解理论上允许编译器将对象标记为"正在被销毁",并且如果两次进入同一对象的析构函数,则允许编译器让守护程序飞出您的鼻孔。然而,检查这个错误不是强制性的,编译器编写者通常很懒(这不是对程序员的侮辱),因此不太可能出现这个检查(除非启用了一些特殊的额外调试选项)。
总结一下:它能永远循环吗?是的。它会崩溃吗?确定。它能停止告诉我一个对象被销毁了两次吗?当然可以。它可以很好地终止程序(即不设置任何错误代码)?是的,那也是。
任何事都可能发生。墨菲说,只要对你造成最大的伤害,就会发生这种情况……例如,当您开发程序时,程序每次都会很好地终止……当你在一千名潜在客户面前做演示时,它会在你脸上严重崩溃。
就是不要这样做:-)
它没有办法知道什么时候停止,所以它可能会无限地运行。
您可能应该编写一个List
类,它具有指向(或实际的)Node
的指针。Node
的d'tor应该只处理它自己的字段,在本例中是mContact
。List
的d'tor应该遍历列表中的所有节点(记住何时停止),并删除每个节点(正好一次)。
假设您将mNext初始化为null,它将不会无限运行。Delete在遇到空指针时什么也不做。因此,它将在您期望的时间结束。
我不知道你在做什么"如果以前"选项。那些没用的。这将是一个有效的节点,因此有一个前一个节点,或者它将不是一个有效的节点,检查前一个将得到未定义的结果。坚持使用简单的答案:
class Node
{
Node( mNext = NULL; );
~Node( )
{
delete mNext; //deallocs next node
}
Contact mContact;
Node* mPrevious;
Node* mNext;
};
说明:此解决方案有效,但必须满足两个条件:1)没有节点在列表中出现两次。2)名单不是循环的如果你能保证这些条件,这就是你最简单的答案。如果你不能,你需要做一些更复杂的事情。
我个人认为Node
的析构函数应该与其他节点有任何关系,这有点奇怪。
如果设计由我决定,我会创建一个List
类,其中包含指向Node
对象(first
和last
)的指针。List
类的析构函数负责遍历列表中的所有节点并销毁它们。
这其实很简单。假设1)它是一个双链表,而不是一个循环链表2)链表中没有环路:这是一个双链表3)实现类只有一个Node实例(可能称为HeadNode或LinkList),这是显式销毁的节点
示例:LinkList为1->2->3->4->5->6->NULL对HeadNode的构造函数调用(参考第三个假设)将导致如下递归调用:删除(1)->删除(2)-> delete(3) ->删除(4)->删除(5)->删除(6)-> NULL所以请检查如果(mNext != NULL)删除mNext它工作了:)
但是:如果你想特别删除一个节点:比如说我们想在上面的例子中只删除4个节点,所有的节点将被删除直到NULL,所以在删除之前请确保你将Mnext设置为NULL。
最佳实践是使用STL库或使用自动指针类来处理问题的销毁部分
- 使用std::list创建循环链表
- 遍历链表时的无限循环
- 循环链表:无限循环
- 打印循环链表
- C++循环链表删除,计数从下一个节点开始
- C++ 循环链表的递归基本情况
- 循环链表的内存错误:未分配正在释放的指针
- 返回指向循环链表某个点的指针
- 在 for 循环(链表)中删除两次后,变量不可用
- 尝试拆分循环链表.显示输出但崩溃.在 IDEone 中显示运行时错误
- 如何打印出双向循环链表
- 循环队列和循环链表
- 我想在我的代码中实现一个双重循环链表 - 运行时错误
- 用C++打印出循环链表
- 需要帮助编码迭代器的循环链表在c++
- 获取单个非循环链表中指针后面节点的值
- 删除循环链表中的节点
- 循环链表的循环开始节点
- 如何将链表转换为循环链表
- STL deque是作为循环链表实现的