2013-07-15 3 views
0

У меня есть следующий метод в C++, который удаляет только элементы, связанные с определенным tableId с карты.Итерация по карте C++, дающая бесконечный цикл

69 void 
70 ObjectFinder::flush(uint64_t tableId) { 
71 
72  RAMCLOUD_TEST_LOG("flushing object map"); 
74  // find everything between tableId, 0 
75  // keep scanning util find all the entries for that table 
76  std::map<TabletKey, ProtoBuf::Tablets::Tablet>::const_iterator it; 
79  for (it = tableMap.begin(); it != tableMap.end(); it++) { 
80   TabletKey current = it->first; 
81   if (tableId == current.first) { 
82    tableMap.erase(current); 
83   } 
84  } 
85  std::cout << "hello" << std::endl; 
87 } 

Шагая в код с gdb я обнаружил, что бесконечный цикл происходит после итерации цикла for. Строка 85 никогда не печатается. Я предполагаю, что свисающий указатель происходит. В первом цикле элемент current удален, а затем в следующих двух ничего не происходит, а затем у меня есть бесконечный цикл. Я не знаю, почему это происходит. У кого-то есть идея или она испытала это раньше?

Еще умнее версия моего кода является использование lower_bound и upper_bound, чтобы найти, где id начинается (это позволит сэкономить некоторое вычислительное время):

69 void 
70 ObjectFinder::flush(uint64_t tableId) { 
71 
72  RAMCLOUD_TEST_LOG("flushing object map"); 
     KeyHash keyHash = Key::getHash(tableId, "", 0); 
74  // find everything between tableId, 0 
75  // keep scanning util find all the entries for that table 
76  std::map<TabletKey, ProtoBuf::Tablets::Tablet>::const_iterator lower; 
     std::map<TabletKey, ProtoBuf::Tablets::Tablet>::const_iterator upper; 
     TabletKey key(tableId, keyHash); 

     lower = tableMap.lower_bound(key); 
     upper = tableMap.upper_bound(key); 
     tableMap.erase(lower, upper); 
85  std::cout << "hello" << std::endl; 
87 } 

и я получаю:

/home/ribeiro.phillipe/ramcloud/src/ObjectFinder.cc:81: error: no matching function for call to ‘std::map<std::pair<long unsigned int, long unsigned int>, RAMCloud::ProtoBuf::Tablets_Tablet, std::less<std::pair<long unsigned int, long unsigned int> >, std::allocator<std::pair<const std::pair<long unsigned int, long unsigned int>, RAMCloud::ProtoBuf::Tablets_Tablet> > >::erase(std::_Rb_tree_const_iterator<std::pair<const std::pair<long unsigned int, long unsigned int>, RAMCloud::ProtoBuf::Tablets_Tablet> >)’ 
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../include/c++/4.4.6/bits/stl_map.h:566: note: candidates are: void std::map<_Key, _Tp, _Compare, _Alloc>::erase(typename std::_Rb_tree<_Key, std::pair<const _Key, _Tp>, std::_Select1st<std::pair<const _Key, _Tp> >, _Compare, typename _Alloc::rebind<std::pair<const _Key, _Tp> >::other>::iterator) [with _Key = std::pair<long unsigned int, long unsigned int>, _Tp = RAMCloud::ProtoBuf::Tablets_Tablet, _Compare = std::less<std::pair<long unsigned int, long unsigned int> >, _Alloc = std::allocator<std::pair<const std::pair<long unsigned int, long unsigned int>, RAMCloud::ProtoBuf::Tablets_Tablet> >] 
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../include/c++/4.4.6/bits/stl_map.h:581: note:     typename std::_Rb_tree<_Key, std::pair<const _Key, _Tp>, std::_Select1st<std::pair<const _Key, _Tp> >, _Compare, typename _Alloc::rebind<std::pair<const _Key, _Tp> >::other>::size_type std::map<_Key, _Tp, _Compare, _Alloc>::erase(const _Key&) [with _Key = std::pair<long unsigned int, long unsigned int>, _Tp = RAMCloud::ProtoBuf::Tablets_Tablet, _Compare = std::less<std::pair<long unsigned int, long unsigned int> >, _Alloc = std::allocator<std::pair<const std::pair<long unsigned int, long unsigned int>, RAMCloud::ProtoBuf::Tablets_Tablet> >] 
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../include/c++/4.4.6/bits/stl_map.h:596: note:     void std::map<_Key, _Tp, _Compare, _Alloc>::erase(typename std::_Rb_tree<_Key, std::pair<const _Key, _Tp>, std::_Select1st<std::pair<const _Key, _Tp> >, _Compare, typename _Alloc::rebind<std::pair<const _Key, _Tp> >::other>::iterator, typename std::_Rb_tree<_Key, std::pair<const _Key, _Tp>, std::_Select1st<std::pair<const _Key, _Tp> >, _Compare, typename _Alloc::rebind<std::pair<const _Key, _Tp> >::other>::iterator) [with _Key = std::pair<long unsigned int, long unsigned int>, _Tp = RAMCloud::ProtoBuf::Tablets_Tablet, _Compare = std::less<std::pair<long unsigned int, long unsigned int> >, _Alloc = std::allocator<std::pair<const std::pair<long unsigned int, long unsigned int>, RAMCloud::ProtoBuf::Tablets_Tablet> >] 
make: *** [obj.master/ObjectFinder.o] Error 1 

является потому что у меня нет версии C++, которая поддерживает это?

+2

При удалении элемента с карты итератор недействителен. Вы можете «tableMap.erase (current ++)» (постинъекция важна) с циклом while – nijansen

ответ

9

Ваш код имеет неопределенное поведение, потому что вы используете итератор, который вы только что признали недействительным. Нравится ли это так:

for (it = tableMap.begin(); it != tableMap.end();) 
{ 
    if (tableId == it->first.first) { tableMap.erase(it++); } 
    else       { ++it; } 
} 
+0

Kerrek, могу ли я продолжить и задать вам другой вопрос? Это связано с этой проблемой, но вместо того, чтобы перебирать карту, я использую upper_bound и ключ, чтобы найти, где он начинается. – cybertextron

+0

@philippe: отредактируйте свой вопрос, и я попытаюсь его решить. –

+0

Я обновил свой вопрос ... – cybertextron

1
tableMap.erase(current); 

Эта линия invalidates the iteratorit. Используя его впоследствии, вы получаете неопределенное поведение.

Перед стиранием этого элемента необходимо выполнить итератор. Вам нужно будет использовать что-то вроде tableMap.erase(it++);, а затем будьте осторожны, чтобы пропустить регулярное приращение цикла.

+0

R. Martinho, подробнее о 'tableMap.erase (tableId);'? – cybertextron

+0

Я думаю, что тип ключа - это пара ...? –

+0

@philippe О, забудьте об этом. Я неправильно прочитал код и не заметил, что ключ был парой. Спасибо Керреку. –

Смежные вопросы