Я пишу программу, которая обрабатывает пакеты обновлений в графе для новых узлов и ребер. Недавно я включил схему скользящего окна, которая проверяет, есть ли в окне ребра, уже находящиеся на графике, и, если нет, удаляет их. Я использую Edge и Node класс следующим образом:
class Edge
{
public:
uint64_t source;
uint64_t target;
unsigned type;
std::string label;
uint64_t timestamp;
bool directed;
bool extracted;
Edge(){}
Edge(Edge *e);
Edge(uint64_t, uint64_t, unsigned, std::string, time_t, bool);
bool operator ==(const Edge *other)
{
return((this->source==other->source)&&(this->target==other->target)&& \
(this->type==other->type));
}
};
class Node
{
public:
uint64_t id;
unsigned type;
std::string label;
uint64_t timestamp;
std::vector<Edge *> adjacent_edges;
Node(){}
Node(Node *);
bool push_edge(Edge *e)
{
try
{
adjacent_edges.push_back(e);
}
catch(std::bad_alloc&)
{
std::cout<<"Error pushing edge"<<std::endl;
return false;
}
return true;
}
std::vector<Edge *>::iterator pop_edge(std::vector<Edge *>::iterator e_it)
{
return adjacent_edges.erase(e_it);
}
bool operator ==(const Node *other)
{
return (this->id == other->id);
}
};
При использовании одного набора данных я получаю сегментарную ошибку после обработки 69 пакетных файлов с размером скользящего окна 5 при попытке получить доступ к ребру с помощью итератора ребра. При использовании другого набора данных я получаю segfault после 69 пакетных файлов при попытке удалить непустой указатель Edge в списке смежности (при попытке освободить память). Я нахожусь в конце своего остроумия, пытаясь выяснить, что происходит не так. Не скользящая оконная версия этой программы работает просто отлично. Также я знаю, что использование структуры данных STL deque было бы лучше для скользящих окон. Тем не менее, я работаю с довольно большим кодом, и я хотел бы быть в состоянии решить эту проблему без использования deque. Заранее спасибо.
Редактировать:
Это происходит на двух разных линиях:
for (int i = 0; i < node_list.size(); i++)
{
vector<Edge *>::iterator adj_it;
for (adj_it = (node_list[i])->adjacent_edges.begin(); adj_it != (node_list[i])->adjacent_edges.end(); ++adj_it )
{if ((max_batch_num - (*adj_it)->timestamp) > time_window)
{deleteEdge(adj_it);
num_edges_deleted++;
--adj_it;
}
}
}
Это происходит на линии:
if ((max_batch_num - (*adj_it)->timestamp) > time_window)
на использовании первого набора данных. Проблема здесь заключается в том, что, хотя вектор не пуст, указатели в векторе указывают на память, которая не является частью приложения. Когда я использую GDB, чтобы попытаться распечатать:
print (*adj_it)->timestamp
это дает: Попытка взять адрес значения не в памяти
Этого не должно происходить, так как ребра добавляются в список смежности. И при использовании второго набора данных ошибка происходит, когда я использую:
delete (*adj_it);
где adj_it является итератором для вектора adjacency_list.
Что также странно, так это то, что если я увеличу скользящее окно, скажем, «n», то же самое произойдет после «n» пакетов.
Добавление функции deleteEdge:
vector<FSM::Edge *>::iterator FSM::Graph::deleteEdge(vector<Edge *>::iterator e_it)
{
//cout<<"Deleting edge: e "<<e->source<<" -> "<<e->target<<endl;//DEBUG
FSM::Node *s = getNode((*e_it)->source);
FSM::Edge *e_tmp = (*e_it);
e_it = s->pop_edge(e_it);
if (e_tmp != NULL)
{
delete e_tmp;
}
else
{
std::cerr<<"Trying to delete an Edge pointer which is NULL"<<endl;
exit(1);
}
return e_it;
}
Также я ранее использовал только индексы, и я попробовал это снова после ответа @Julius. Это мой новый цикл удаления.
for (int j = 0; j<(node_list[i])->adjacent_edges.size();j++)
{
if ((max_batch_num - ((node_list[i])->adjacent_edges[j])->timestamp) > time_window)
{
(node_list[i])->adjacent_edges.erase((node_list[i])->adjacent_edges.begin() + j);
--j;
num_edges_deleted++;
}
}
Однако я получаю одинаковые ошибки независимо от.
КСТАТИ. Я действительно ценю все комментарии до сих пор. Спасибо за ваше время.
Изменить: Обнаружены утечки памяти в другой части кода с использованием valgrind. Избавление от этого кода (он не был действительно необходим для алгоритма) избавилось от него. Я принимаю ответ @Julius, так как это решило бы проблему согласно моему первоначальному утверждению. Также спасибо @RetiredNinja, @Beta и @Golazo за отличные комментарии.
for (int i = 0; i < node_list.size(); i++)
{
vector<Edge *>::iterator adj_it;
for (adj_it = (node_list[i])->adjacent_edges.begin(); adj_it != (node_list[i])->adjacent_edges.end(); ++adj_it )
{if ((max_batch_num - (*adj_it)->timestamp) > time_window)
{deleteEdge(adj_it);
num_edges_deleted++;
--adj_it;
}
}
}
Вы удаляете ребро, затем возвращаетесь назад с —adj_it, затем повторяете назад по ребру, которое вы только что удалили с помощью deleteEdge, потому что цикл for имеет ++ adj_it. Затем вы пытаетесь проверить объект метки времени удаленного (недопустимого) объекта Edge, что вызывает segfault.
Либо это, либо вы удаляете объект из вектора Edge *, а затем делаете недействительным свой итератор.
Важной частью является то, что итераторы не являются индексами. Вы не можете просто стереть элемент, а затем выполнить —adj_it. В данном случае использование индекса будет проще, так как вы можете просто удалить объект ребра, удалить указатель ребра из вектора, а затем продолжить цикл после выполнения —adj_it, как вы это делаете. Кстати, итераторы медленнее, чем индексы на векторах.