C ++ удаление указателя из 2 наборов указателей - PullRequest
0 голосов
/ 13 октября 2019

У меня есть 2 набора указателей, из которых я хочу удалить определенный указатель. Но это дает мне двойную ошибку в последней строке функции удаления края.

Сначала я подозревал, что, возможно, это произошло потому, что set::erase() неявно вызывал delete, но я прочитал некоторые документы и обнаружил, чтодеструктор не будет вызван, потому что edge_ins и edge_outs являются обоими наборами указателей на края.

Теперь я не уверен, что вызывает ошибку двойного освобождения при втором вызове set::erase()поэтому я пришел сюда за помощью.

У меня есть подозрение, что это может быть из-за того, что указатели обрабатываются как объекты, а когда я стираю тот же указатель из root->edge_outs.erase(e), он уже исчезает, поэтому происходит сбой из-за второгопозвоните по номеру root->edge_ins.erase(e). Но я не смог найти ничего полезного до сих пор.

Удаление края:

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);
}

Может быть уместно, поэтому я также добавлю, как я выделяю память дляthe Edge.

Создание Edge:

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;
}

UPDATE : я изменил код, чтобы не разыменовывать удаленный объектно все равно выдает двойную ошибку.

Новый код:

SDG::delete_edge(std::set<Edge*>::iterator e)
{
    (*e)->head->rm_in(e);
    (*e)->root->rm_out(e);
    delete *e;
}

Ответы [ 2 ]

0 голосов
/ 13 октября 2019

Проблема решена.

Проблема заключалась в том, что эта 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++))

0 голосов
/ 13 октября 2019

Вам не следует делать это:

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>) вместо необработанных указателей. Тогда вам не нужно выполнять управление памятью самостоятельно, то есть вызывать удаление, потому что память будет управляться умным указателем. Общий указатель удалит память в куче при уничтожении последнего общего указателя на этот ресурс. Затем вы можете изменить функцию следующим образом:

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>>. Вот пример того, как создать общий указатель. В вашем случае вы можете создать его следующим образом в функции edge ():

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

Использование общих указателей позволит избежать проблемы двойного удаления, которая, кажется, происходит в вашем коде. В противном случае вам придется самостоятельно управлять памятью и избегать двойного удаления указателя.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...