【问题标题】:Destructor for circular linked list in c++?c++中循环链表的析构函数?
【发布时间】:2020-10-07 16:03:00
【问题描述】:

当 'class LL' ~LL() 的析构函数被这个循环单链表调用时,程序崩溃而不是释放指针的堆空间。我该如何解决这个问题?

class Node {
public:
  int data;
  Node *next;
};

class LL {
private:
  Node *head, *tail;

public:
  LL() {
    head = NULL;
    tail = NULL;
  }
  // destructor
  ~LL() {
    Node *p = head;
    while (p->next != head) {
      p = p->next;
    }

    while (p != head) {
      p->next = head->next;
      delete head;
      head = p->next;
    }

    if (p == head) {
      delete head;
      head = nullptr;
    }
  }

  // circular singly Linked list

  void createLL() {
    int n, x;
    cin >> n;

    for (int i = 0; i < n; i++) {
      cin >> x;

      Node *t = new Node;
      t->data = x;
      t->next = NULL;

      if (head == NULL) {
        head = tail = t;
      } else {
        tail->next = t;
        tail = t;
      }
    }
    tail->next = head;
  }

【问题讨论】:

  • 尝试在调试器中运行您的程序并逐行执行您的代码。错误信息是什么?崩溃后的堆栈跟踪是什么?
  • 也画图。没有什么能像图片一样帮助可视化链接列表中应该发生的事情。然后,在使用调试器单步执行时,这些图片将作为您的预期。一旦你发现程序在做你没有画的事情,你就发现了一个错误。
  • 首先,如果列表是循环的,则不需要head - 如果两者都存在,则它总是 head == tail-&gt;next,否则两者都是NULL
  • @MooingDuck append 需要尾指针才能不扫描整个列表。
  • @CiaPan 不,不是。该列表是循环的。您使用以下之一:头部、尾部或哨兵。您不需要超过一个。

标签: c++ linked-list destructor


【解决方案1】:

链表存在一些问题。

  1. 链表的析构函数假定 head 不为空,但有可能是。在尝试清理内存之前,请确保检查 head 不为空。完成后,您的原始析构函数看起来应该可以工作了。
  2. 如果用户输入小于或等于 0 的大小,函数 createLL 将调用未定义的行为。 特别是这一行tail-&gt;next = head;
  3. TreatLL 是用词不当,因为它实际上并没有按照预期的意义“创建”一个新列表。内容不会被清除,因此n 元素会附加到当前列表的末尾。
  4. 此外,循环链表可以只用一个尾指针创建。

但是,让循环链表的实现工作看起来像这样

#include <iostream>
using namespace std;

class Node {
public:
    int data;
    Node* next;
};
class LL {
private:
    Node* head, * tail;
public:
    LL() : head(nullptr),
        tail(nullptr) {
    }
    ~LL() {
        if (head) {
            Node* p = tail;

            while (p != head) {
                p->next = head->next;
                delete head;
                head = p->next;
            }
            if (p == head) {
                delete head;
                head = nullptr;
            }
        }
    }

    void storeUserInput() {
        int n, x;
        cin >> n;
        if (n <= 0) {
            return; //no input to retrieve.
        }
        for (int i = 0; i < n; i++) {
            cin >> x;
            Node* t = new Node;
            t->data = x;
            t->next = nullptr;

            if (head == nullptr) {
                head = tail = t;
            }
            else {
                tail->next = t;
                tail = t;
            }
        }
        tail->next = head;
    }

};
int main() {
    LL l;
    l.storeUserInput();

    char response;
    std::cin >> response;
}

您似乎可以访问 C++ 11 或更高版本的编译器,如果是,那么您应该使用 nullptr 代替 NULL,因为它是确定的指针类型。查看更多here

【讨论】:

    【解决方案2】:

    您可以分两步完成:

    • 使列表非圆形。这有两个子步骤:
      • 检测循环。有已发布的算法可以执行此操作。 编辑:您的列表有一个尾指针,因此在您的情况下无需搜索它。
      • 将反向引用节点指向 null(或 sentinel)
    • 在循环中删除现在非循环的列表。这是微不足道的。

    【讨论】:

    • 另外,他不需要把它变成非圆形的。他可以反复删除head,直到列表为空。
    • @MooingDuck 但是他们必须在每次删除时更新 tail->next ,这样它就不会悬空。更快地先设置下一个,然后查找您设置的内容。
    【解决方案3】:

    尝试在循环中删除时,您的循环引用将导致内存被删除并且具有未定义的行为。所以首先考虑打破循环:

    tail->next = 0;
    

    然后循环删除

    Node* p = head;
    while(p)
    {
        Node* temp = p;
        p = p->next;
        delete temp;
    }
    

    顺便说一句。 tail->next 将始终指向头部。所以你总是会在同一个指针中同时拥有头部和尾部。所以你可以像这样清理内存:

    Node* p = tail->next; //this is head
    tail->next = 0;
    while(p)
    {
        Node* temp = p;
        p = p->next;
        delete temp;
    }
    

    【讨论】:

    • @MooingDuck 让我检查一下。
    猜你喜欢
    • 2018-07-22
    • 2018-03-27
    • 1970-01-01
    • 2015-08-03
    • 2019-04-09
    • 2016-01-31
    • 2013-03-18
    • 2013-04-26
    相关资源
    最近更新 更多