删除包含其他节点地址的节点
Deleting node containing address of other node
我知道下面给出的代码中的逻辑是错误的,但我怀疑当我们删除具有下一个节点地址的 p 时会发生什么。
析构函数会做什么?
它会去所有节点使它们为空,直到节点 p 中的 Next 不变为 null
还告诉我析构函数对内存做了什么。
我已经阅读了很多关于 destructor、deallocation、delete 和 free 的文章,但我仍然感到困惑。
主要的困惑在于释放和析构函数。
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 的对象实例,然后“释放”内存,以便它可以用于其他目的。
对于你的代码问题,你释放了之前为你的节点“NEXT”保留的内存。
C++ 中的每个对象都以构造函数调用开始其生命,并以析构函数结束。对于原始类型,这是空操作。对于 classes,您可以定义两个调用的作用。
这与有两个对象的存储正交:
- 自动 存储意味着对象的生命周期是根据定义对象的范围自动推断的。 IE。 C++ 自动确保为对象调用构造函数和析构函数*。这包括:
- 局部变量:执行时调用的Ctor传递了定义,Dtor在作用域的末尾调用——大致是'}'。
- Local static vars: Ctor 与之前相同但只是第一次,Dtor 在程序末尾且仅当 Ctor 已被调用时。
- 全局变量: Ctor在
main
之前调用,大部分是无序的,Dtor在程序末尾。
- Member vars: class 的初始化列表中的 Ctor。 Dtor 在 class 的 Dtor 的末尾。即使在这里,两个函数总是被调用,这意味着你不应该也不能在 class 的 dtor 中显式调用 dtor。
- 成员静态变量:与全局变量相同。
- 动态存储 - 必须通过使用
new
明确请求。 C++ 永远不会为你调用 delete
,你有责任只调用 一次。 delete
调用析构函数 + 释放由 new
分配给存储的内存。 不足以 显式地调用析构函数。此外 delete
不会修改指针,如果代码依赖于此,则必须将其显式设置为 nullptr
**。
*除非程序崩溃。
**不要使用 NULL
.
在您的示例中,Node::~Node
正确地销毁了之前由 new
分配的 next
内存。 int data
在 dtor 的末尾自动销毁,因为它是原始类型,所以它是空操作。 dtor 完成后,Node
对象被销毁。
但这不是销毁链表的正确方法,因为析构函数是递归的,对于较长的列表很容易溢出堆栈。正确的程序是手动遍历 dtor 中的列表和 delete
各个节点。
是的,Node
的析构函数将销毁它后面的所有节点,不,它不会使任何东西成为空指针(如果它在访问 object 销毁后未定义)。
您的 deleteAlternateNodes
在访问被破坏的节点时有未定义的行为。
一个快速而肮脏的解决方法是在销毁节点之前切断“尾巴”:
q->next = p->next; // Save the tail.
p->next = nullptr; // Cut off the tail.
delete p; // This is safe now.
修复剩余的错误作为练习。
(先用笔和纸画出列表来计算。这是开发和调试代码的最佳方法 pointer-based。)
在这条语句中删除操作符
delete p;
指针p
指向的节点之后的所有节点(由于数据成员next
)将被删除。
所以函数 deleteAlternateNodes
调用了未定义的行为,因为表达式 q->next
指向的所有节点都被分配为
q->next = p->next;
将因该声明而被删除
delete p;
所以这个声明
p = q->next;
将指针 p
设置为无效指针。
这是一个演示程序
#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 << "null\n";
}
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 语句,因为对于空指针,不会调用析构函数。例如
~Node()
{
std::cout << "The destructor is called for node with data equal to "
<< data << '\n';
delete next;
}
或
~Node()
{
delete next;
}
我知道下面给出的代码中的逻辑是错误的,但我怀疑当我们删除具有下一个节点地址的 p 时会发生什么。
析构函数会做什么? 它会去所有节点使它们为空,直到节点 p 中的 Next 不变为 null
还告诉我析构函数对内存做了什么。 我已经阅读了很多关于 destructor、deallocation、delete 和 free 的文章,但我仍然感到困惑。 主要的困惑在于释放和析构函数。
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 的对象实例,然后“释放”内存,以便它可以用于其他目的。
对于你的代码问题,你释放了之前为你的节点“NEXT”保留的内存。
C++ 中的每个对象都以构造函数调用开始其生命,并以析构函数结束。对于原始类型,这是空操作。对于 classes,您可以定义两个调用的作用。
这与有两个对象的存储正交:
- 自动 存储意味着对象的生命周期是根据定义对象的范围自动推断的。 IE。 C++ 自动确保为对象调用构造函数和析构函数*。这包括:
- 局部变量:执行时调用的Ctor传递了定义,Dtor在作用域的末尾调用——大致是'}'。
- Local static vars: Ctor 与之前相同但只是第一次,Dtor 在程序末尾且仅当 Ctor 已被调用时。
- 全局变量: Ctor在
main
之前调用,大部分是无序的,Dtor在程序末尾。 - Member vars: class 的初始化列表中的 Ctor。 Dtor 在 class 的 Dtor 的末尾。即使在这里,两个函数总是被调用,这意味着你不应该也不能在 class 的 dtor 中显式调用 dtor。
- 成员静态变量:与全局变量相同。
- 动态存储 - 必须通过使用
new
明确请求。 C++ 永远不会为你调用delete
,你有责任只调用 一次。delete
调用析构函数 + 释放由new
分配给存储的内存。 不足以 显式地调用析构函数。此外delete
不会修改指针,如果代码依赖于此,则必须将其显式设置为nullptr
**。
*除非程序崩溃。
**不要使用 NULL
.
在您的示例中,Node::~Node
正确地销毁了之前由 new
分配的 next
内存。 int data
在 dtor 的末尾自动销毁,因为它是原始类型,所以它是空操作。 dtor 完成后,Node
对象被销毁。
但这不是销毁链表的正确方法,因为析构函数是递归的,对于较长的列表很容易溢出堆栈。正确的程序是手动遍历 dtor 中的列表和 delete
各个节点。
是的,Node
的析构函数将销毁它后面的所有节点,不,它不会使任何东西成为空指针(如果它在访问 object 销毁后未定义)。
您的 deleteAlternateNodes
在访问被破坏的节点时有未定义的行为。
一个快速而肮脏的解决方法是在销毁节点之前切断“尾巴”:
q->next = p->next; // Save the tail.
p->next = nullptr; // Cut off the tail.
delete p; // This is safe now.
修复剩余的错误作为练习。
(先用笔和纸画出列表来计算。这是开发和调试代码的最佳方法 pointer-based。)
在这条语句中删除操作符
delete p;
指针p
指向的节点之后的所有节点(由于数据成员next
)将被删除。
所以函数 deleteAlternateNodes
调用了未定义的行为,因为表达式 q->next
指向的所有节点都被分配为
q->next = p->next;
将因该声明而被删除
delete p;
所以这个声明
p = q->next;
将指针 p
设置为无效指针。
这是一个演示程序
#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 << "null\n";
}
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 语句,因为对于空指针,不会调用析构函数。例如
~Node()
{
std::cout << "The destructor is called for node with data equal to "
<< data << '\n';
delete next;
}
或
~Node()
{
delete next;
}