删除时出现分段错误
Posted
技术标签:
【中文标题】删除时出现分段错误【英文标题】:Segmentation fault when deleting 【发布时间】:2020-07-29 14:34:26 【问题描述】:我是 C++ 的新手,我正在尝试实现一个树结构,但我遇到了一个分段错误,该错误在删除树时出现。 这段代码很简单,我有一个 Node 类,其中包含指向其子节点的指针。
#include <vector>
class Node
public:
int data1, data2;
std::vector<Node*> children;
Node* add_child(double data1, double data2)
Node* n = new Node(data1, data2);
children.push_back(n);
return n;
Node(double data1, double data2)
:data1(data1), data2(data2)
~Node()
for(auto child : children)
delete child;
children.clear();
;
int main()
Node root = Node(0, 0);
Node* n = &root;
for(int i = 0; i < NB; ++i)
n = n->add_child(0, 0);
main 创建了一个很简单的结构,但是有错误就足够了。仅当 NB 的值大于 170 000 时才会发生 seg 错误。
【问题讨论】:
递归销毁和 170 000 个节点听起来你可能会溢出堆栈。 销毁发生在析构函数中,而不是在那个循环中。而且销毁是递归的。 树结构不应该被递归删除,而不是根节点应该以非递归方式收集所有后代,清除它们的children
向量,然后删除这些节点。
@t.niese 我应该有一个remove
方法来做到这一点吗?我需要能够删除所有节点,而不仅仅是根。然后这个方法会收集它所有的孩子,然后调用每个孩子的析构函数(这将是空的)?
【参考方案1】:
基于non-recursive program to delete an entire binary tree 示例,我们可以如下修改您的应用程序:
#include <iostream>
using namespace std;
#include <vector>
#include <queue>
class Node
public:
Node* add_child(double data1, double data2)
Node* n = new Node(data1, data2, false);
children.push_back(n);
return n;
Node(double data1, double data2, bool is_root = true )
:data1(data1), data2(data2), is_root(is_root)
~Node()
if( is_root ) deleteTree();
size_t get_num_childs()
return children.size();
Node* get_child( size_t index )
return children[index];
private:
/* Non-recursive function to delete an entire tree. */
void deleteTree()
// Create an empty queue for level order traversal
queue<Node *> deleteQueue;
// Do level order traversal starting from root
for( int index = 0; index < children.size(); index++ )
deleteQueue.push( children[index]);
while( !deleteQueue.empty())
Node *node = deleteQueue.front();
deleteQueue.pop();
for( int index = 0; index < node->get_num_childs(); index++ )
// Add all childs to queue for deleting
deleteQueue.push( node->get_child( index ));
delete node;
private:
int data1, data2;
std::vector<Node*> children;
bool is_root;
;
int main()
Node root = Node(0, 0);
Node* n = &root;
for(int i = 0; i < 17000; ++i)
n = n->add_child(0, 0);
return 0;
在cmets中提到,递归调用节点析构函数会导致堆栈溢出,进而导致segmentation fault。
因此,为了解决这个问题,我们必须坚持使用非递归删除节点。这是在deleteTree()
方法的支持下完成的。为此,我们在内部构建了一个包含所有需要删除的节点的队列。
【讨论】:
抱歉,这不是好的代码。在 C++ 中,new
与 delete
配对,构造函数与析构函数。你的析构函数不起作用,你需要一个额外的deleteRoot
。这个想法很合理,它应该被整合到Node::~Node
中。为此,您需要确保delete node
不会触发递归删除,方法是在将node->children
添加到deleteQueue
后清除它们。
@MSalters 您是对的,感谢您的反馈,根据您的评论更正了程序。
你真的不需要is_root
。如果deleteTree
在delete node
之前清除了node
的孩子,那么通过delete node
对node->deleteTree
的递归调用是无操作的。以上是关于删除时出现分段错误的主要内容,如果未能解决你的问题,请参考以下文章