在n-ary树中创建/访问节点时内存泄漏

时间:2015-07-01 11:42:19

标签: c++ struct memory-leaks constructor tree

我从以下代码中获取内存泄漏:

struct Node {
    Node *children[20];
    int value;

    Node();
    ~Node();
};

Node::Node() {
    for(int i=0; i<20; i++) {
        children[i] = NULL;
    }
}

void Node::insert(int x) {
    Node *n = this;
    for(int i=0; i<20; i++) {
        if(n->children[i] == NULL) {
            n->children[i] = new Node();
            n->children[i]->value = x;
        }
        n = n->children[i];
    }
}  

用Valgrind检查,显然我从这一行得到了泄漏:

n->children[i] = new Node();

这是使用构造函数的错误方法吗?
或者Valgrind检查误导?

Valgrind错误讯息:

505 (448 direct, 57 indirect) bytes in 2 blocks are definitely lost in loss record 2 of 3
  at 0x4C2B1C7: operator new(unsigned long)
  by 0x401A4F: Node::insert
  by 0x4015FA: main

LEAK SUMMARY:
  definitely lost: 448 bytes in 2 blocks
  indirectly lost: 57 bytes in 2 blocks
    possibly lost: 0 bytes in 0 blocks
  still reachable: 72,704 bytes in 1 blocks
       suppressed: 0 bytes in 0 blocks

ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 2 from 2)  

我有的析构函数:

Node::~Node() {
    for(int i=0; i<20; i++) {
        delete children[i];
        children[i] = NULL;
    }
}

主要:

int main() {
    Node *n = new Node();

    .
    .
    .

    delete n;
    n = NULL;

    return 0;
}

3 个答案:

答案 0 :(得分:1)

您不是要删除孩子。正如Axel所回答的,你应该在你的析构函数中添加一个删除。或者更好的是,使用unique_ptr数组,当它们超出范围时,它将负责销毁节点。

答案 1 :(得分:0)

你永远不会删除子指针。

您应该在Node类析构函数中删除子数组的每个已分配指针。

就像那样:

Node::~Node()
{
    for (int i = 0; i < 20; ++i)
    {
        delete children[i];
    }
}

此外,如果有任何其他方法替换子数组中的指针,则必须在替换之前删除前一个指针,如下所示:

...
delete n->children[i];
n->children[i] = new Node();
...

...
delete n->children[i];
n->children[i] = p;  // where p is a pointer of Node
...

有关信息,您必须调用 delete 来销毁和释放您在堆上构造的每个对象(使用 new )。

您还可以使用smartpointer来避免管理儿童成员的破坏。

答案 2 :(得分:0)

我看到一个新的,但没有删除,也没有智能指针。这意味着您可以在不释放内存的情况下分配内存。 (至少你没有告诉我们Node的析构函数应该在哪里完成。)

除此之外,您的代码中存在一些缺陷:

  • 你对n的使用看起来很可疑,目前尚不清楚它的作用是正确的还是仅仅是一个bug。您可能希望至少通过评论或更好地通过使用其他功能和有意义的名称来澄清。
  • 一旦找到NULL指针并使用新节点填充整个数组,就不会停止,这意味着您可以成功插入exaclty一次
  • 您默认构造一个节点并立即为其赋值 - 可能您应该编写一个相应的构造函数。

使用智能指针分配内存:

struct Node {
    unique_ptr<Node> children[20]; //consider switching to std::array
    int value;

    // no destructor needed, because unique_ptr takes care of itself    
    Node (int newValue);
    void insert(int valueToInsert);
};


void Node::insert(int valueToInsert) {
    auto freeSpot = //find the point where you want to insert the new node
    if ( /*freeSpot is a valid location*/ )
        *freeSpot = make_unique<Node>(x);
}  

Node::Node(int newValue) 
  : value(newValue)
{}

这将是你可以做的第一次重构