地图上的 C++ 循环未检测到地图末端的变化

C++ loop on map not detecting change of map`s end

我在遍历地图 (std::map) 时遇到问题。

在我的循环中,调用了一个函数,该函数有时(并非总是)擦除同一地图的元素。使用此功能后,有一些代码使用此地图信息作为输入。

此函数删除任何元素后我没有遇到任何问题,除了 unique 地图的 last 元素是已删除。

我的循环似乎无法理解地图的最后一个元素与它开始运行时不一样,并且会尝试对不存在的元素进行操作,造成崩溃。

在我看来,循环描述中的 myMap.end() 调用无法使用地图的新 end() 进行自我更新。

代码的相关部分如下:

for(std::map<int, ConnectionInfo>::iterator kv = myMap.begin(); kv != myMap.end(); ++kv) {
        int thisConnectionID=kv->first; //This is where I get garbage when the loop enters when it shouldnt;
        ConnectionInfo currentConnectionInfo=kv->second; //This is where I get garbage when the loop enters when it shouldnt;
        status=eraseSomeMapElementsIfNecessary(thisConnectionID,currentConnectionInfo.DownPacket); //this function might erase elements on myMap. This generates no problems afterwards, except when the end element of myMap is erased
        ... //Next parts of the code make no further usage of myMaps, so I just hid it not to pollute the code
}

我的解释是 kv != myMap.end() 无法理解内部循环正在更改(擦除)myMap 的最后一个元素(结束)吗?

在这种情况下,我该如何解决这个问题?

或者我的理解有误,解决方案与我之前所说的无关?

感谢您的帮助!

I am having no problems after this function erases any elements, except on the unique case that the last element of the map is erased.

擦除任何容器中的元素都会使其迭代器失效。之后你递增无效的迭代器。

您应该在删除迭代器指向的元素之前递增迭代器。

如果您不知道在循环内运行的哪些元素会被擦除,则假定所有迭代器都已失效。

迭代可能删除元素的映射时的常用习惯用法是:

for(auto it = map.begin(); it != map.end(); ) {
   if ( *it == /*is to delete*/ ) {
     it = map.erase(it);
   }
   else
     ++it;
}

如果您的 eraseSomeMapElementsIfNecessary 可能会删除正在迭代的地图中的一些随机值,那么这肯定会导致问题。如果 it 引用的元素被删除,它就变得无效,然后用 ++it 递增 it 也是无效的。

问题实际上只与 it 迭代器有关,如果 eraseSomeMapElementsIfNecessary 擦除它然后您使用它 - 您有未定义的行为 (UB)。所以解决方案是将当前迭代器传递给 eraseSomeMapElementsIfNecessary,然后 return 从它传递下一个迭代器:

it = eraseSomeMapElementsIfNecessary(it);

我示例中的 for 循环主体应该在您的 eraseSomeMapElementsIfNecessary 函数中。至少这是一个解决方案。

也许这 2 个链接会有所帮助:

  • How can I delete elements of a std::map with an iterator?

基本上,归根结底,您必须在迭代器失效之前对其进行更新。

你必须在擦除当前迭代器之前保留下一个迭代器;因为删除元素后当前的将失效

auto nextit = it+1;
map.erase(it);
it = nextit;