C++ 尝试从链接列表中删除节点时出现分段错误
我正在尝试创建一个方法(C++ 尝试从链接列表中删除节点时出现分段错误,c++,C++,我正在尝试创建一个方法(delete\u at)来删除给定位置的节点,但是我得到了一个分段错误(核心转储)错误。奇怪的是,代码工作正常,有效地删除了我想要的节点,但我一直收到错误。错误在处的delete\u中,其他方法工作正常 有人能帮我吗 #include <iostream> #include <stdlib.h> using namespace std; //each of the nodes of our list is a class template <
delete\u at
)来删除给定位置的节点,但是我得到了一个分段错误(核心转储)错误。奇怪的是,代码工作正常,有效地删除了我想要的节点,但我一直收到错误。错误在处的delete\u中,其他方法工作正常
有人能帮我吗
#include <iostream>
#include <stdlib.h>
using namespace std;
//each of the nodes of our list is a class
template <class T> class node {
private:
T data;
public:
node <T> *next;
node(): data(NULL), next(NULL) {}
node(T x): data(x), next(NULL) {}
T get_data () {
return (data);
}
};
template <class T> class list {
private:
node <T> *head, *p, *last;
public:
list(): head(NULL), p(NULL), last(NULL) {}
~list () {
node <T> *n;
for (p = head; p != NULL; p = p->next) {
n = p->next;
delete (p);
p = n;
}
delete (n);
cout << "list destroyed." << endl;
}
bool is_empty () {
return (head == NULL);
}
//inserts a node at the beginning of the list
void append (T x) {
p = new node <T> (x);
if (head == NULL) {
head = p;
last = head;
}
else {
p->next = head;
head = p;
}
}
//inserts a node at the end of the list
void push (T x) {
p = new node <T> (x);
if (head == NULL) {
head = p;
last = head;
}
else {
last->next = p;
last = p;
}
}
void pop () {
if (is_empty())
cout << "empty list.";
else if (head == last)
head = last = NULL;
else {
for (p = head; p->next != last; p = p->next);
delete (last);
last = p;
last->next = NULL;
}
}
void delete_at (int i) {
if (is_empty())
cout << "empty list.";
else if (i < size()) {
p = head;
if (i == 0) {
head = p->next;
delete (p);
}
else {
for (int j=0; j < i-1; j++)
p = p->next;
node <T> *tmp = p->next;
if (tmp->next != NULL) {
p->next = tmp->next;
}
else {
last = p;
last->next = NULL;
}
delete (tmp);
}
}
else
cout << "Invalid index." << endl;
}
void print () {
for (p = head; p != NULL; p = p->next)
cout << p->get_data() << " ";
cout << endl;
}
int size () {
int size = 0;
for (p = head; p != NULL; size++, p = p->next);
return (size);
}
T operator [] (int index) {
int i;
for (i=0, p = head; i < index; p = p->next, i++);
return (p->get_data());
}
};
int main () {
//initializing random seed
srand (time(NULL));
list <int> l;
cout << "Is the list empty? " << l.is_empty() << endl;
l.push(rand() % 100);
l.push(rand() % 100);
l.push(rand() % 100);
l.append(rand() % 100);
l.push (rand() % 100);
cout << "Is the list still empty? " << l.is_empty() << endl;
cout << "l[2] = " << l[2] << endl << endl;
cout << "size of the list: " << l.size() << endl;
l.print ();
l.pop ();
l.print();
l.delete_at (0);
l.print ();
l.delete_at (10);
return (0);
}
#包括
#包括
使用名称空间std;
//列表中的每个节点都是一个类
模板类节点{
私人:
T数据;
公众:
节点*下一步;
node():数据(NULL),下一个(NULL){}
节点(tx):数据(x),下一个(NULL){
无法获取_数据(){
返回(数据);
}
};
模板类列表{
私人:
节点*head,*p,*last;
公众:
list():head(NULL)、p(NULL)、last(NULL){}
~list(){
节点*n;
for(p=head;p!=NULL;p=p->next){
n=p->next;
删除(p);
p=n;
}
删除(n);
cout-next=p;
last=p;
}
}
void pop(){
如果(是空的())
cout next!=last;p=p->next);
删除(最后一次);
last=p;
last->next=NULL;
}
}
在(int i)处作废删除_{
如果(是空的())
下一步;
删除(p);
}
否则{
对于(int j=0;jnext;
节点*tmp=p->next;
如果(tmp->next!=NULL){
p->next=tmp->next;
}
否则{
last=p;
last->next=NULL;
}
删除(tmp);
}
}
其他的
无法获取_数据());
}
};
int main(){
//初始化随机种子
srand(时间(空));
清单l;
你的代码中有很多错误。一次只做一小步,然后进行大量测试
以下是一些问题和建议:
- 如果列表为空并且调用析构函数,则将删除未初始化的n
- 在析构函数中,每次循环迭代前进两次(在for语句中前进一次,在循环块中前进一次),但只删除一项。因此,只有大约一半的项被正确删除
- 如果您解决了上述两个问题,则不需要在循环后删除n
- 只要可能,您的变量应该在第一次使用时初始化
- 在
delete\u at
函数中,首先询问大小没有多大意义,因为它会导致列表重复两次。特别是,如果i
为0,则长度没有多大用处
- 如果您在(0)处调用
delete\u
以获取包含单个项目的列表,您的代码将不会正确更新last
,也不会将head
重置为nullptr
- 将指针
prev
初始化为nullptr
并前进直到达到所需索引可能会容易得多。此时,您知道是否必须更新头部(prev
仍然为零)和/或尾部(next
为零以便删除项)
- 如注释中所述,
p
不应是成员变量
- 在
pop
中,如果列表包含单个项目,则不会删除该项目
- 考虑到内部状态可能不正确(例如,
last
notupdated),很难预测几步后是否会出现问题
- 您应该删除节点类(以及您的列表)的复制和移动构造函数ans分配,以确保不会意外调用它们
- 您的默认构造函数不初始化数据。由于未使用它,您可能会考虑删除它。
另外,由于在main
中的调用点处有2个delete\u,因此您的问题无法说明错误何时发生。提供您得到的输出会很有用
我认为没有必要添加5项来获得崩溃,因此删除一些“更新”以查看崩溃是否仍然发生,这将加快对实际状态的推理
我建议您进行一些单元测试。您应该添加一个对象计数器或对构造函数/析构函数的输出调用,这样有助于检测缺失的删除
对于您的测试,您可以执行以下操作:
- 创建并销毁空列表
- 创建、推送项目、销毁列表
- 创建、推送项目、弹出项目、销毁列表
- 创建、推送项目、删除索引0处的项目、销毁列表
- 通过追加项目而不是推送项目重复此操作
- 创建一个空列表并调用pop,删除其上的\u
- 添加多个项目并删除部分或所有项目
对于您的测试,您只需使用数据的特定值进行测试
,并检查您在某个点上是否具有预期的项(以及是否已正确删除适当数量的节点).学习使用调试器。它将直接指向错误发生的位置。析构函数是错误的。它跳过节点,并可能由于取消引用空指针而导致未定义的行为。另一个提示--在测试类时不要使用随机数据。原因是,如果类不工作,使用随机数据只会导致错误更难。只要先使用已知值——一旦你的类开始工作,你就可以使用随机数据。p
不应该是该类的成员。它只在成员函数的循环中使用,实际上并不存储该类的任何数据,所以当你真的需要它时,就在循环中声明它。你应该使用nullptr
而不是<代码>空值