c++写单链表,析构函数delete触发断点,如何修复?
问题应该是指针越界,如何修改?
、
源代码:
#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 等类似工具来检测程序运行时的内存情况。
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;
}
问题分析:
while (head->next != 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;
}
if (head->next == NULL)
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;
}