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

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;
}