C ++从2组指针中删除一个指针

时间:2019-10-13 06:48:42

标签: c++ pointers set double-free

我有2套指针,我想从中删除某个指针。但这在删除边缘功能的最后一行给了我一个双重释放错误。

我最初怀疑可能是因为set::erase()隐式调用了delete,但是我阅读了一些文档,发现因为edge_ins和{{1 }}都是指向Edges的指针。

现在,我不确定是什么原因导致第二次致电edge_outs时出现双重释放错误,所以我来这里寻求帮助。

我怀疑这可能是因为指针被当作对象对待,并且当我从set::erase()擦除相同的指针时,它已经消失了,因此由于在root->edge_outs.erase(e)的第二次调用而崩溃了。但是到目前为止,我找不到任何有用的东西。

边缘删除:

root->edge_ins.erase(e)

可能相关,因此我还将添加如何为Edge分配内存。

边缘创建:

std::set<Edge*>::iterator
SDG::delete_edge(std::set<Edge*>::iterator e)
{
    delete *e;
    (*e)->head->edge_ins.erase(e);
    return (*e)->root->edge_outs.erase(e);
}

更新: 我已经更改了代码,以免取消引用已删除的对象,但仍然会出现双重释放错误。

新代码:

Edge&
SDG::edge(Vertex* out, Vertex* in, Edge::Type type)
{
    // 新しいエッジを生成
    Edge* edge = new Edge(type, *out, *in);

    // 頂点からエッジへの参照
    out->add_out(edge);
    in->add_in(edge);

    return *edge;
}

2 个答案:

答案 0 :(得分:0)

您不应这样做:

delete *e;
(*e)->head->edge_ins.erase(e);
return (*e)->root->edge_outs.erase(e);

一旦删除了指针,您就不应尝试取消对其的引用,否则会遇到分段错误或某些其他未定义的行为。考虑像这样更改语句的顺序和delete_edge的返回类型:

void SDG::delete_edge(std::set<Edge*>::iterator e)
{
    (*e)->head->edge_ins.erase(e);
    (*e)->root->edge_outs.erase(e);
    delete *e;
}

这将在删除指针本身之前从集合中删除指针。

您应该考虑使用智能指针(std::shared_ptr<Edge>)代替原始指针。然后,您不必自己进行内存管理,即调用delete,因为内存将由智能指针管理。当销毁该资源的最后一个共享指针时,共享指针将删除堆上的内存。然后您可以像这样更改功能:

void SDG::delete_edge(std::set<std::shared_ptr<Edge>>::iterator e)
{
    (*e)->head->edge_ins.erase(e);
    (*e)->root->edge_outs.erase(e);
}

,并且集合应为std::set<std::shared_ptr<Edge>>类型。这是如何创建共享指针的example。您可以在函数edge()中按如下方式创建它:

std::shared_ptr<Edge> edge = std::make_shared<Edge>(type, *out, *in);

使用共享指针将避免重复删除的问题,这似乎发生在您的代码中。否则,您必须自己进行内存管理,并避免两次删除指针。

答案 1 :(得分:0)

问题解决了。

问题在于此delete_edge函数将std::set<Edge*>::iterator中的edge_outs用作参数。然后继续从另一组edge_ins中删除元素,从而导致未定义的行为,从而导致双重释放。

要解决此问题,我从根本上更改了delete_edge函数,使其采用了Edge*,以使函数的用户不会感到困惑。

至于返回值,它最初打算用于在程序迭代时删除edge_outs的元素。因此,我需要一个使用iterator = edge_outs.erase(iterator)的循环,但我也想同时从edge_ins中删除相同的元素。因此,我想出了一个功能,可确保正确删除边缘,但似乎我第一次尝试失败。

以下是该函数的更好版本:

void
SDG::delete_edge(Edge* e)
{
    e->head->edge_ins.erase(e);
    e->root->edge_outs.erase(e);
    delete e;
}

使用此功能时,在修复之前,应像这样使用

iterator = delete_edge(iterator)

但是当您执行以下操作时,我意识到修复后具有相同的效果:

delete_edge(*(iterator++))

相关问题