es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

¿Por qué borrar un elemento del mapa mientras se está recorriendo no provoca un bloqueo cada vez que ocurre?

Aquí está mi función:

void Timers::RemoveTimer(DWORD id)
{
    auto it = m_mapTimers.begin();
    for ( ; it != m_mapTimers.end(); ++it) {
        if (it->first.second == id) {
            m_mapTimers.erase(it);
        }
    }
}

Y claramente no debería eliminar el elemento de esta forma, pero lo que me interesa es por qué el fallo no es constante. El programa puede llamar a esta función varias miles de veces hasta que se cae.

Además, como una pregunta secundaria, ¿por qué no hubo fallos en C++98? Los obtengo después de portar a C++20, sin cambios en la clase.

Tags:  , , ,

Answer

  1. Avatar for davy.ai

    La función dada Timers::RemoveTimer() atraviesa el mapa m_mapTimers utilizando un iterador it, y elimina el elemento si el segundo valor de la clave coincide con el id dado. Sin embargo, al eliminar un elemento de un mapa se invalidan todos los iteradores que apuntan a ese elemento o a cualquier otro elemento posterior. Por lo tanto, cuando el bucle continúa iterando después de eliminar un elemento, it ahora apunta a un iterador invalidado.

    En C++98, este problema podría no haber causado fallas porque el estándar no era tan estricto en cuanto a lo que provoca que un iterador se invalide. Sin embargo, en C++20, el estándar se ha vuelto más riguroso en cuanto a las situaciones en las que un iterador se invalida, lo que lleva a una falla en este caso.

    Para resolver este problema, el bucle debería utilizar std::map::erase() directamente en el elemento que coincide con el id dado, y actualizar el iterador it en consecuencia utilizando el valor de retorno de erase(). Aquí está la función actualizada:

    void Timers::RemoveTimer(DWORD id)
    {
        auto it = m_mapTimers.begin();
        while (it != m_mapTimers.end()) {
            if (it->second == id) {
                it = m_mapTimers.erase(it);
            }
            else {
                ++it;
            }
        }
    }
    

    Esta función revisada elimina correctamente el elemento que coincide con el id dado, sin invalidar ningún iterador.

Comments are closed.