我知道下面给出的代码中的逻辑是错误的,但我怀疑当我们删除具有下一个节点地址的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作为练习
(先用笔和纸画清单。这是开发和调试基于指针的代码的最佳方法。)