c++写单链表,析构函数delete触发断点

c++写单链表,析构函数delete触发断点,如何修复?
问题应该是指针越界,如何修改?

img

源代码:



#include 
using namespace std;

//带头节点的单链表
template<class T>
struct Node
{
    T date;
    Node*next;
};

template<typename T>
class headLinkList
{
public:
    //成员函数
    headLinkList()
    {
        length = 0;
        head = new Node;
        head->next = 0;
    }
    //头插法构造
    headLinkList(T a[], int n);
    //尾插法构造
    headLinkList(T a[], int n, int b);
    //按关键字大小顺序插入
    void seqinsert(T x);
    //查找
    Node* GetElem(headLinkList list, T x);
    //获取链表长度
    int GetLength(headLinkList list)
    {
        cout << "length= " << length << endl;
        return length;
    }

    T Delete(int n);
    void show()
    {
        if(head->next==NULL)throw"链表为空";
        Node*p = head->next;
        while (!p->next)
        {
            cout << p->date << " ";
            p = p->next;
        }cout <date<< endl;
    }
    
    Node* GetElem(Node*head,int num)//得到第num位的节点地址
    {
        if (head->next == NULL)throw"链表为空";
        Node*p = head;
        while (p->next != NULL)
        {
            p = p->next;
        }return p;
        
    }
    ~headLinkList()
    {

        Node *tempptr;
            while (head->next != NULL)
            {
                tempptr = head->next;
                delete head;
                head = tempptr;
            }
            delete head;

    }
private:
    Node*head;
    int length;
};

template<class T>
headLinkList::headLinkList(T a[], int n)
{
    head = new Node;
    head->next = NULL;
    for (int i = 0; i < n; i++)
    {
        Node*p = new Node;
        p->date = a[i];
        p->next = head->next;
        head->next = p;
        length++;
    }
}

template<class T>
headLinkList::headLinkList(T a[], int n, int b)
{
    head = new Node;
    head->next = NULL;
    Node*p = head;
    for (int i = 0; i < n; i++)
    {
        Node*temp = new Node;
        temp->date = a[i];
        p->next = temp;
        p = temp;
        length++;
        /*p->next = NULL;*/
    }

}

template <class T>
void headLinkList::seqinsert(T x)
{
    if(head->next==NULL)throw"链表为空"

    Node*p = head;
    Node*temp = head;
    Node*x = new Node;
    x->date = x;
    while (p->next)
    {
      p = p->next;
      if (x->date <= p->date)
      {
          temp->next = x;
          x->next = p;
          length++;
          return 0;
      }
      temp = temp->next;
    }
    p->next = x;
    length++;
    x->next = NULL;

}

template<typename T>
T headLinkList::Delete(int n)
{ 
    /*if (n > length || n < 1)throw"链表为空";*/
        Node*p = head;
    int i = 0;
    while (i-1)
    {
        p = p->next;
        i++;
    }
    Node*x = p->next;
    p->next = x->next;
    T l = x->date;
    delete x;
    length--;
    x = NULL;
    cout << "delete了" << l << endl;
    return 0;

}

template<class T>
Node* headLinkList::GetElem(headLinkList list, T x)
{
    if (head->next == Null)throw"链表为空"
        Node*p = head;
    while (p->next)
    {
        p = p->next;
        if (p->date == x)
        {
            return p;
        }
    }
    return 0;
}


int main()
{
    try {
        int arr[] = { 1,2,5,9,7 };
        cout << "test" << endl;
        //头插法构造函数
        headLinkList<int> mode1(arr, 5);
        //尾插法构造函数
        headLinkList<int>mode2(arr, 5, 0);
        //遍历链表
        cout << "mode1:"; mode1.show(); 
        cout << endl;
        cout << "mode2:"; mode2.show();
        cout << endl;
        
        mode1.Delete(2);

        mode2.GetLength(mode2);
    }
    catch (const char*s)
    {
        cout << s << endl;
    }
}

参考GPT和自己的思路:在析构函数中,您试图删除头结点和每个节点,但是当您删除头结点时,您将头指针设置为下一个节点,这可能会导致您在迭代过程中遗漏某些节点。因此,建议您使用临时节点指针来删除每个节点,而不是直接删除头结点。

此外,您在函数 seqinsert 中声明了参数 T x,但是在函数内部又声明了变量 Node*x,这会导致编译错误。建议您更改参数名称,以避免名称冲突。

最后,函数 GetElem 中的 if 语句中有一个拼写错误(Null 应更正为 NULL)。

以下是修改后的代码:

template<class T>
headLinkList<T>::~headLinkList()
{
    Node<T> *tempptr;
    while (head->next != NULL)
    {
        tempptr = head->next;
        delete head->next; // 修改1:删除 head 的下一个节点而不是 head 自己
        head->next = tempptr->next;
    }
    delete head; // 修改2:在循环外部删除 head 节点
}


这样修改后,~headLinkList() 析构函数可以正常删除链表中所有的节点。

里面有几处错误,先改改试试。第一个问题会造成类型浅拷贝,可能是造成析构函数的问题
第一个

int GetLength(headLinkList list)  改成引用模式 int GetLength(headLinkList &list) 

第二个

headLinkList<T>::headLinkList 构造函数里面少了对length的初始化,length=0

第三个

    void show()
    {
        if(head->next==NULL)throw"链表为空";
        Node<T>*p = head->next;
        while (p->next)  //===== ! 去掉

第四个

void seqinsert(T x); 这个函数里面名字为x变量定义了2个,需要改下,date赋值有问题

你构造链表的时候就不对

headLinkList<T>::headLinkList(T a[], int n)
{
    head = new Node<T>;
    head->next = NULL;
    for (int i = 0; i < n; i++)
    {
        Node<T>* p = new Node<T>;
        p->next = nullptr;//加上这一句,否则他的next是随机数,最后一个节点的next必然不为NULL,删除必出错,下一个构造函数亦如是修改
        p->date = a[i];
        p->next = head->next;
        head->next = p;
        length++;
    }
}


     ~headLinkList()
    {
 
        //Node<T> *tempptr;//如果只有头节点会造成野指针
        Node<T> *tempptr = head;
            while (head->next != NULL)
            {
                head = head->next;//移动头节点
                delete tempptr;    //删除前节点
                head = tempptr;    //更新tempptr指针
            }
            delete head;
            //为了安全起见,建议加上,建议c++里面遇到空指针问题,多用nullptr,少用c的风格,像ptr=NULL或ptr=0这种
          tempptr=head=nullptr;
 
    }

head本身没有被分配,指向的是没有初始化的地址,是不是已经被释放了

参考GPT和自己的思路,在析构函数headLinkList::~headLinkList()中,删除节点时没有更新 head 指针,导致在下一次循环中 delete head; 时触发断点。

为了修复这个问题,我们需要更新 head 指针以便在下一次循环中正确地删除节点。具体来说,我们可以在 while 循环中添加一个额外的指针 prev,它指向当前节点的前一个节点,这样在删除当前节点时,我们可以更新 prev->next 指向下一个节点,并将当前节点指针移动到下一个节点,最后更新 head 指针指向当前节点。以下是修复后的代码:

template<typename T>
headLinkList<T>::~headLinkList()
{
    Node<T> *curr = head->next;
    Node<T> *prev = head;
    while (curr != NULL)
    {
        prev->next = curr->next;
        delete curr;
        curr = prev->next;
    }
    delete head;
}

此外,在 show() 函数中,应该使用 while (p != NULL),而不是 while (!p->next) 来遍历链表,因为最后一个节点的 next 指针是 NULL,而不是非空。

该回答引用ChatGPT

如有疑问,可以回复我!

在析构函数中,您在删除每个节点后立即将head指针指向下一个节点。然而,当您删除最后一个节点并将head指针设置为NULL后,还会试图执行一次“delete head;”,这会导致断点。

为了解决这个问题,请修改析构函数以确保在删除最后一个节点后不再尝试删除。这是修改后的析构函数:

~headLinkList()
{
    Node<T> *tempptr;
    while (head != NULL)
    {
        tempptr = head->next;
        delete head;
        head = tempptr;
    }
}


此外,GetElem 函数中的 Null 应更改为 NULL,因为 Null 在C++中是未定义的。将以下行:

if (head->next == Null)throw"链表为空"

更改为:

if (head->next == NULL)throw"链表为空";

还有一个问题是在 seqinsert 函数中,您在函数参数中已经有了一个名为 x 的变量,但是您又在该函数中定义了一个名为 x 的新 Node。这会导致编译错误。您应该将新节点的名称更改为 newNode,并相应地更新函数中的引用:


template <class T>
void headLinkList<T>::seqinsert(T x)
{
    if(head->next==NULL)throw"链表为空";
 
    Node<T>*p = head;
    Node<T>*temp = head;
    Node<T>*newNode = new Node<T>;
    newNode->date = x;
    while (p->next)
    {
      p = p->next;
      if (newNode->date <= p->date)
      {
          temp->next = newNode;
          newNode->next = p;
          length++;
          return;
      }
      temp = temp->next;
    }
    p->next = newNode;
    length++;
    newNode->next = NULL;
}

现在,您的程序应该可以正常运行,不会再出现断点问题。

在析构函数中,您使用了一个while循环来删除链表中的每个节点。但是,在循环内部,您将head指针更新为下一个节点的地址,这会导致在第二次迭代时访问已经被删除的内存位置,从而触发断点。

要修复这个问题,您应该保留一个指向当前节点和下一个节点的指针,并稍微修改您的while循环来使它正确地更新这些指针。以下是您可以使用的新的析构函数代码:

~headLinkList()
{
    Node<T> *current = head->next;
    while (current != NULL)
    {
        Node<T> *temp = current->next;
        delete current;
        current = temp;
    }
    delete head;
}

这样,您将能够正确地删除所有节点而不会触发断点。
```c++

哥哥在析构函数中,你的代码使用了指向被删除节点的指针,这是不安全的。通常,应该保存一个临时指针来访问下一个节点,然后才能删除当前节点。所以,在 while 循环体内,我们需要使用另一指针将删除节点的下一个节点保存起来。

修改 headLinkList 的析构函数如下:

template<typename T>
headLinkList<T>::~headLinkList()
{
    Node<T>* p = head->next;
    while (p != NULL)
    {
        Node<T>* temp = p;
        p = p->next;
        delete temp;
    }
    delete head;
}

在代码中尽量避免使用“delete”语句,可以使用智能指针 RAII 机制自动释放内存。同时在定义链表时,最好考虑使用工具来检测内存泄漏和越界等问题,比如利用 valgrind 等类似工具来检测程序运行时的内存情况。

  • 另外在析构函数中,while循环条件应该是head!=NULL而不是head->next!=NULL。因为删除链表节点时需要让head指向下一个节点,如果一直用head->next判断循环是否结束,那么head指向的节点已经被删除了,这样就会访问非法内存。修改后的代码如下:
~headLinkList()
{
    Node<T>* tempPtr;
    while (head != NULL)
    {
        tempPtr = head->next;
        delete head;
        head = tempPtr;
    }
}

还有,在seqinsert()方法中,应该将新节点x加到temp节点之后,再让x指向p节点,否则链表无法正确连接。修改后的代码如下:

template<class T>
void headLinkList<T>::seqinsert(T x)
{
    if (head->next == NULL)throw"链表为空";
    Node<T>* p = head;
    Node<T>* temp = head;
    Node<T>* newNode = new Node<T>;
    newNode->date = x;
    while (p->next)
    {
        p = p->next;
        if (newNode->date <= p->date)
        {
            temp->next = newNode;
            newNode->next = p;
            length++;
            return;
        }
        temp = temp->next;
    }
    p->next = newNode;
    length++;
    newNode->next = NULL;
}
问题分析:
根据代码和错误提示,可以看出问题出现在析构函数中的 delete 操作上,具体来说是在 while 循环中的 delete 操作触发了断点。这种情况通常是由于指针越界或者重复释放内存导致的。
在析构函数中,我们需要释放链表中所有节点的内存,因此需要使用 while 循环遍历链表,并逐个删除节点。在删除节点时,我们需要先保存节点的下一个节点的指针,然后再删除当前节点。如果没有保存下一个节点的指针,那么在删除当前节点后,我们就无法访问下一个节点了,这就会导致指针越界的问题。
另外,如果我们重复释放了同一个节点的内存,那么也会触发断点。因此,在删除节点时,我们需要确保每个节点只被释放一次。
解决方案:
针对指针越界的问题,我们需要修改 while 循环中的条件判断语句,确保在访问节点指针之前,先判断该指针是否为 NULL。具体来说,我们可以将 while 循环的条件判断语句修改为:

while (head->next != NULL)

针对重复释放内存的问题,我们需要在删除节点之前,将节点的指针设置为 NULL,以避免重复释放。具体来说,我们可以在 while 循环中的删除节点操作之后,将节点的指针设置为 NULL,如下所示:

tempptr = head->next;
head->next = tempptr->next;
tempptr->next = NULL;
delete tempptr;

修改后的析构函数代码如下:

~headLinkList()
{
Node *tempptr;
while (head->next != NULL)
{
tempptr = head->next;
head->next = tempptr->next;
tempptr->next = NULL;
delete tempptr;
}
delete head;
}

另外,需要注意的是,在 GetElem 函数中,我们需要修改 if 判断语句中的 NULL,将其改为大写字母,即:

if (head->next == NULL)

修改后的 GetElem 函数代码如下:

template
Node* headLinkList::GetElem(headLinkList list, T x)
{
if (head->next == NULL)
throw "链表为空";
Node*p = head;
while (p->next)
{
p = p->next;
if (p->date == x)
{
return p;
}
}
return 0;
}