删除时出现分段错误

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++ 中,newdelete 配对,构造函数与析构函数。你的析构函数不起作用,你需要一个额外的deleteRoot。这个想法很合理,它应该被整合到Node::~Node中。为此,您需要确保delete node 不会触发递归删除,方法是在将node-&gt;children 添加到deleteQueue 后清除它们。 @MSalters 您是对的,感谢您的反馈,根据您的评论更正了程序。 你真的不需要is_root。如果deleteTreedelete node 之前清除了node 的孩子,那么通过delete nodenode-&gt;deleteTree 的递归调用是无操作的。

以上是关于删除时出现分段错误的主要内容,如果未能解决你的问题,请参考以下文章

删除模板数组时出现分段错误[重复]

将结构插入地图时出现分段错误

分配时出现分段错误[重复]

删除以结构为键的地图时出现分段错误

在 C++ 中使用向量时出现分段错误

分段错误:在 C++ 中弹出向量时出现 11