【问题标题】:Dr. Memory: are these lines really causing of memory leaks?Dr. Memory:这些线真的会导致内存泄漏吗?
【发布时间】:2019-06-16 00:45:45
【问题描述】:

我有以下代码:

class Node {
    public:
        int data;
        Node* parent;
        std::unique_ptr<Node> left;
        std::unique_ptr<Node> right;
    public:       
        explicit Node(int d) : data(d),
                               parent(nullptr),
                               left(nullptr),
                               right(nullptr) { }
        /*
            Some functions
        */                  
};

class dt {
    private:            
        Node* root;
    private:        
        void add_helper(Node* parent, Node* node); 
        /*
                Some helper functions
        */      
    public:
        dt() : root(nullptr) {}
        ~dt() {
            delete root;
        } 
        /*
                Some functions
        */       
        void add(int data);        
};


void dt::add(int data) {
    if(!root) {
        root = new Node(data); // 1: memory leak
        return;
    }   
    Node* node = new Node(data); // 2: memory leak
    add_helper(root, node);
}

当我扫描此代码以查找内存泄漏时 Dr.内存 在上面的 cmets 中提到的行上显示错误。但它们真的是内存泄漏吗? dt 具有析构函数,其中 root 指针正在删除。当析构函数开始删除 root 节点时,它将递归地删除所有子节点。还是发生了其他事情?

【问题讨论】:

  • 看不到内存泄漏,但我肯定会看到大量内存损坏,并且几乎肯定会崩溃,如果您尝试实际删除具有至少几个的树的根节点层次结构的层次。我强烈怀疑其余未显示的代码实际上从未尝试删除整个树。
  • ...@SamVarshavchik 所说的内容,请将其设为minimal reproducible example,以便我们进行相同的分析。
  • 为什么Node析构函数会删除它的父级?通常模式是让每个节点删除其子节点(在您的情况下,这是通过 std::unique_ptr 的 dtor 发生的,这很好)
  • Ted Lyngmo - 同意将发布最小的工作示例。
  • @ՎարդանԳրիգորյան 没关系,慢慢来。将来,请准备好获得相同的要求,因此首先将您的问题压缩为可以复制、编译和分析的代码,而无需填写任何空白。

标签: c++ pointers memory-leaks destructor dr-memory


【解决方案1】:

这些行真的会导致内存泄漏吗?

Node* node = new Node(data) 行上分配的内存可能会泄漏。至少该示例没有显示指针将被删除的位置。所有权可能会在add_helper 内转移,但这已从示例中排除。

我不熟悉 Dr. Memory,但我假设它会报告泄漏内存的分配位置;不是泄露的地方。


此外,如果您复制dt 实例,程序的行为将是不确定的,因为原始和副本都将尝试删除复制的指针。

要修复潜在的内存泄漏和潜在的 UB,请使用唯一指针。就像你使用 Node 一样。

我不能将 unqiue_ptr 用于 root,因为有多个指向同一个节点的指针。

通过在析构函数中执行delete root;,您的类将假定指针的唯一所有权。如果该假设是正确的,那么使用唯一指针就没有问题。如果假设不正确,那么析构函数可能会导致未定义的行为。使用它而不是唯一指针不是解决方案。您可能需要改用共享指针。

最后,除非你的树是平衡的,否则隐式递归析构函数会线性增加最坏情况的递归深度,这很容易导致堆栈溢出。您应该平衡树(例如使用 red-back 或 AVL 增强)或使用迭代算法来销毁子树。

【讨论】:

  • 但是当 root 为 null 时,它会从函数返回而不执行行:Node* node = new Node(data)。至于指针,我不能使用 unqiue_ptr 作为 root,因为有多个指针指向同一个 Node。
  • eerorika,好的,谢谢。你是对的,所有权转移到 add_helper。将发布 mre 以便更清楚。
  • eerorika- 实际上这里显示的代码部分来自我的 AVL 树实现。
  • @ՎարդանԳրիգորյան 啊。那你就不用担心我回答的最后一段了。
  • @ՎարդանԳրիգորյան 当然,如果你有一个循环,那么某个时候的弱指针会阻止所有权变得循环。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多