2013-07-15 90 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_boundupper_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

從映射中刪除元素時,迭代器失效。你可以通過一個while循環'tableMap.erase(current ++)'(postincrement很重要) – 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哦,忘了那個。我誤解了代碼,沒有注意到鑰匙是一對。謝謝Kerrek。 –