C++ Error al borrar última casilla de un vector con iteradores

Iniciado por Orubatosu, 3 Diciembre 2014, 17:22 PM

0 Miembros y 3 Visitantes están viendo este tema.

Orubatosu

Bueno... lo reconozco, no se que he hecho mal


    it=v.begin();
    double dist;
    for (; it!=v.end(); it++){
      dist = abs (medvec - *it);
      if (dist > med) v.erase(it);
      }


Os pongo en antecedentes, esto es sencillo.

Tengo un vector de números reales, y pretendo borrar del mismo los que estén a mas de una distancia específica de la media. No parece problemático, pero ahaha...

El error me aparece en esta línea:


if (dist > med) v.erase(it);


Supongamos 5 valores: 1.0, 2.0, 3.0, 4.0, 5.0

Los valores que cumplen con la condición del if son el primero y el último

El primero es borrado, pero cuando el programa llega a intentar borrar la última posición del vector, simplemente "revienta".

Si cojo en otra parte del programa, creo un iterador para ese vector, lo posiciono en la posición del "5.0" y hago un erase(it)... funciona, no me da el problema.

Precisamente al usar iteradores, asumo que este nunca va a ponerse fuera de rango, ya que en el bucle if especifico claramente que "mientras it != v.end() con lo que no puedo suponer (en principio) que estoy intentando borrar una casilla inaccesible del vector.

He hecho algunas comprobaciones obviamente, y si por ejemplo limito el bucle a una vuelta menos ( it != v.end()-1) entonces el programa funciona sin errores, y se para tras llegar al "4.0"

El problema llega a la hora de intentar borrar la última casilla, cosa que aparentemente si puedo hacer "a lo bruto".

Si cambio el "erase" por un "cout << *it" veo que efectivamente, los números que cumplen la condición son los adecuados.

La verdad es que estoy atascado y no tengo claro que es lo que falla.
"When People called me freak, i close my eyes and laughed, because they are blinded to happiness"
Hideto Matsumoto 1964-1998

eferion

si tu haces v.erase( it ), el iterador it queda invalidado, cualquier operación que hagas sobre el mismo después del "erase" será errónea.

Lo que puedes hacer es crear en el momento de borrar un segundo iterador, incrementas el primero y después haces un erase sobre el segundo:

Código (cpp) [Seleccionar]
if (dist > med)
{
  auto it2 = it;
  it++;
  v.erase(it2);
}


Un saludo.

Orubatosu

#2
No, no me funciona, me sigue dando el mismo error.

Además, creo que esto no es así, según la documentación

http://www.cplusplus.com/reference/vector/vector/erase/

Citar
Return value
An iterator pointing to the new location of the element that followed the last element erased by the function call. This is the container end if the operation erased the last element in the sequence.

Es decir: La operación me devuelve un iterador señalando a la posición que sigue al último elemento eliminado. Este será el contenedor "end" si la operación borra el último elemento.

Por otro lado, fíjate en que me borra sin problemas el primer elemento del vector, y tras eso es sometido a varias operaciones de incremento, también sin errores.

Actualizo: Tras hacer varias pruebas mas, veo que el programa me da un error al borrar un segundo elemento del vector, este puede ser contiguo al primero, no tiene porque ser el último.

Modifico el encabezado del post para hacerlo mas coherente con el problema

Y ojo, el copiar ese vector a otro no me ha solucionado el problema ¿?¿?

ACTUALIZO DE NUEVO

Bueno... esto no me lo esperaba... y está solucionado.

Parece que se me ha pasado por alto lo obvio... al borrar un elemento, reduzco la longitud del vector, pero el iterador continua incrementando su valor.

En un concepto un poco "lioso"... pero aparentemente al reducir en una unidad el tamaño, el iterador pasa a la siguiente casilla, pero el bucle lo incrementa nuevamente. No se si me explico bien.

Lo he solucionado de esta manera:


    for (; it!=v.end(); it++){
      dist = abs (medvec - *it);
      if (dist > med){
        v.erase(it);
        it--;
        }
      }


Lo que no tengo tan claro, es si esto es porque mi compilador se está "colando" por algún sitio, o porque en realidad esto es lo que se supone que debemos de hacer.

Especulaciones y explicaciones (si las hay) a partir del punto . <-- (Ese punto)
"When People called me freak, i close my eyes and laughed, because they are blinded to happiness"
Hideto Matsumoto 1964-1998

Eternal Idol

La economía nunca ha sido libre: o la controla el Estado en beneficio del Pueblo o lo hacen los grandes consorcios en perjuicio de éste.
Juan Domingo Perón

Orubatosu

No.. me da error también. El poner el it-- aparentemente arregla el problema, al menos he modificado datos del vector inicial con mas números, y funciona perfectamente.
"When People called me freak, i close my eyes and laughed, because they are blinded to happiness"
Hideto Matsumoto 1964-1998

Eternal Idol

Me parece que no hay ninguna garantia de que it continue siendo valido despues de llamar a erase, mientras que si le asignas el valor de retorno de erase si lo sera, como ya viste la condicion de incremento del for no es conveniente, lo mas sencillo es incrementar solo cuando no borres dentro del bucle.
La economía nunca ha sido libre: o la controla el Estado en beneficio del Pueblo o lo hacen los grandes consorcios en perjuicio de éste.
Juan Domingo Perón

Orubatosu

#6
Técnicamente si... pero si pongo el it=v.erase(it); también me explota.

Estoy por probar (ahora no, que voy a cenar) el no utilizar un bucle for, sino un while e incrementar SOLO si no se borra, a ver que pasa.

Un apunte... veamos un caso simple:


int main() {
    vector<int> A = {1, 2, 3, 4, 5, 6, 7, 8};
    vector<int>::iterator it = A.begin();
    cout << *it;
    A.erase(it);
    cout << *it;
}


Primera salida: "1"... eso es obvio
Borramos la casilla "1"
El contenido del iterador, es ahora "2"

Pero, pero al llegar al terminar  un bucle "for", el valor del iterador se incrementará nuevamente, pasando a contener "3".

Esto no daría ningún problema, ya que al terminar el bucle, el iterador estaría apuntado no la última casilla del vector, sino a A.end();

Pero, si hacemos un nuevo borrado, incrementamos en 1, y luego el bucle en 1 mas... y terminaremos el bucle con el iterador fuera de los límites del vector.

Esa es mi teoría... ¿ideas?... por eso cuando borra, añado un it-- para compensarlo

"When People called me freak, i close my eyes and laughed, because they are blinded to happiness"
Hideto Matsumoto 1964-1998

Eternal Idol

Cita de: Orubatosu en  3 Diciembre 2014, 20:25 PM
Técnicamente si... pero si pongo el it=v.erase(it); también me explota.

Estoy por probar (ahora no, que voy a cenar) el no utilizar un bucle for, sino un while e incrementar SOLO si no se borra, a ver que pasa.

Si, como te decia antes es la manera de hacerlo, asignar e incrementar cuando no hayas borrado.

Ejemplo sencillo:
Código (c++) [Seleccionar]
for (vector<int>::iterator it = v.begin(); it != v.end(); )
{
 if (*it % 2 == 0)
   it = v.erase(it);
  else
    ++it;
}
La economía nunca ha sido libre: o la controla el Estado en beneficio del Pueblo o lo hacen los grandes consorcios en perjuicio de éste.
Juan Domingo Perón

Orubatosu

"When People called me freak, i close my eyes and laughed, because they are blinded to happiness"
Hideto Matsumoto 1964-1998

Eternal Idol

Lo que pasa es que en un momento erase retorna un interator apuntando al fin del contenedor y cuando haces el incremento del bucle el iterator se va al diablo. Otra manera de hacerlo siguiendo el ejemplo anterior:

Código (c++) [Seleccionar]
for (vector<int>::iterator it = v.begin(); it != v.end(); ++it)
{
 if (*it % 2 == 0)
   it = v.erase(it);
 if (it == v.end())
  break;
}
La economía nunca ha sido libre: o la controla el Estado en beneficio del Pueblo o lo hacen los grandes consorcios en perjuicio de éste.
Juan Domingo Perón