正在删除包含其他节点地址的节点



我知道下面给出的代码中的逻辑是错误的,但我怀疑当我们删除具有下一个节点地址的p时会发生什么。

析构函数会做什么?它会去所有的节点使它们为空,直到节点p中的Next没有变为空

还告诉我析构函数对内存的作用。我读过很多关于析构函数、释放、删除和释放的文章,但我仍然很困惑。主要混淆在释放和析构函数中。

class Node {
public:
int data;
Node * next;
Node(int data){
this -> data = data;
this -> next = NULL;
}

~Node() {
if(next) {
delete next;
}
}
};
void deleteAlternateNodes(Node *head) {
//Write your code here
Node *p =head;
Node *q =NULL;
if(p->next == NULL)
{
return;
}
while(p!=NULL)
{
q=p;
p=p->next;
q->next = p->next;
delete p;
p = q->next;
}
}

在操作员的此语句中删除

delete p;

将删除指针p所指向的节点之后的所有节点(由于数据成员next)。

因此函数deleteAlternateNodes调用未定义的行为,因为表达式q->next指向的所有节点都像一样赋值

q->next = p->next;

将由于该语句而被删除

delete p;

所以这个声明

p = q->next;

将指针CCD_ 5设置为无效指针。

这是一个演示程序

#include <iostream>
class Node 
{
public:
int data;
Node * next;

Node(int data)
{
this -> data = data;
this -> next = NULL;
}

~Node() 
{
std::cout << "The destructor is called for node with data equal to "
<< data << 'n';
if(next) 
{
delete next;
}
}
};
void display( const Node *head )
{
for ( ; head; head = head->next )
{
std::cout << head->data << " -> ";
}

std::cout << "nulln";
}
int main() 
{
Node *head = new Node( 1 );
Node *current = head;
current->next = new Node( 2 );
current = current->next;
current->next = new Node( 3 );

display( head );

delete head;

return 0;
}

程序输出为

1 -> 2 -> 3 -> null
The destructor is called for node with data equal to 1
The destructor is called for node with data equal to 2
The destructor is called for node with data equal to 3

事实上,析构函数中的if语句是多余的

if(next) 
{
delete next;
}

你可以写

delete next;

没有if语句,因为对于null指针,将不会调用析构函数。例如

~Node() 
{
std::cout << "The destructor is called for node with data equal to "
<< data << 'n';
delete next;
}

~Node() 
{
delete next;
}

因此,析构函数是销毁对象之前要调用的最后一个函数。它正在销毁(如调用的相关析构函数)传递给删除的对象实例,然后";frees";以便可以将其用于其他目的。

对于您的代码问题,您取消分配之前为您的节点保留的内存";NEXT";。

C++中的每个对象都以构造函数调用开始其生命周期,并以析构函数结束其生命周期。对于基元类型,这是no-op。对于类,您可以定义两个调用的作用

这与对象的存储正交,对象有两个:

  • 自动存储意味着根据定义对象的范围自动推断对象的寿命。也就是说,C++自动确保为对象调用构造函数和析构函数。这包括:
    • 局部变量:执行传递定义时调用Ctor,在作用域末尾调用Dtor,大致为"}">
    • 本地静态变量:Ctor与前一次相同,但仅为第一次,Dtor位于程序末尾,并且仅当Ctor已被调用时
    • 全局变量:在main之前调用的Ctor,大部分是无序的,程序结束时调用的Dtor
    • 成员vars:类的初始值设定项列表中的Ctor。Dtor在班级Dtor的末尾。即使在这里,这两个函数也总是被调用的,这意味着您不应该也绝不能在类的dtor中显式调用dtor
    • 成员静态变量:与全局变量相同
  • 动态存储-必须通过使用new明确请求。C++永远不会为您调用delete,您有责任只调用一次delete调用析构函数+解除分配由new分配给存储器的内存。仅仅显式调用析构函数是不够的。此外,delete不会修改指针,如果代码依赖于此,则必须显式将其设置为nullptr**

*除非程序崩溃。**请勿使用NULL

在您的示例中,Node::~Node正确地销毁了先前由new分配的next内存。int data在dtor结束时自动销毁,因为它是一个基元类型,所以它是no-op。dtor结束后,Node对象将被销毁。

不过,这不是销毁链表的正确方法,因为析构函数是递归的,对于较长的列表,很容易溢出堆栈。正确的过程是手动迭代dtor中的列表和delete中的各个节点。

是的,Node的析构函数将销毁它后面的所有节点,不,它不会使任何东西成为空指针(如果它这样做了,也没有帮助,因为在销毁后访问对象是未定义的)。

您的deleteAlternateNodes在访问已销毁节点时具有未定义的行为
一个快速而肮脏的解决方案是切断";尾部;在摧毁节点之前:

q->next = p->next;  // Save the tail.
p->next = nullptr;  // Cut off the tail.
delete p; // This is safe now.

修复剩下的bug作为练习
(先用笔和纸画清单。这是开发和调试基于指针的代码的最佳方法。)

最新更新