Foro de elhacker.net

Programación => Programación C/C++ => Mensaje iniciado por: Orubatosu en 3 Diciembre 2014, 17:22 pm



Título: C++ Error al borrar última casilla de un vector con iteradores
Publicado por: Orubatosu en 3 Diciembre 2014, 17:22 pm
Bueno... lo reconozco, no se que he hecho mal

Código:
    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:

Código:
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.


Título: Re: C++ Error al borrar última casilla de un vector con iteradores
Publicado por: eferion en 3 Diciembre 2014, 19:00 pm
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
  1. if (dist > med)
  2. {
  3.  auto it2 = it;
  4.  it++;
  5.  v.erase(it2);
  6. }
  7.  

Un saludo.


Título: Re: C++ Error al borrar una segunda casilla de un vector con iteradores
Publicado por: Orubatosu en 3 Diciembre 2014, 19:45 pm
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:

Código:
    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)


Título: Re: C++ Error al borrar última casilla de un vector con iteradores
Publicado por: Eternal Idol en 3 Diciembre 2014, 19:47 pm
Ya lo viste:
it = v.erase(it);


Título: Re: C++ Error al borrar última casilla de un vector con iteradores
Publicado por: Orubatosu en 3 Diciembre 2014, 19:59 pm
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.


Título: Re: C++ Error al borrar última casilla de un vector con iteradores
Publicado por: Eternal Idol en 3 Diciembre 2014, 20:03 pm
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.


Título: Re: C++ Error al borrar última casilla de un vector con iteradores
Publicado por: 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.

Un apunte... veamos un caso simple:

Código:
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



Título: Re: C++ Error al borrar última casilla de un vector con iteradores
Publicado por: Eternal Idol en 3 Diciembre 2014, 20:31 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
  1. for (vector<int>::iterator it = v.begin(); it != v.end(); )
  2. {
  3.  if (*it % 2 == 0)
  4.    it = v.erase(it);
  5.  else
  6.    ++it;
  7. }


Título: Re: C++ Error al borrar última casilla de un vector con iteradores
Publicado por: Orubatosu en 3 Diciembre 2014, 20:35 pm
Ahaha... claro, esto es "lo mismo"... está claro.


Título: Re: C++ Error al borrar última casilla de un vector con iteradores
Publicado por: Eternal Idol en 3 Diciembre 2014, 20:46 pm
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
  1. for (vector<int>::iterator it = v.begin(); it != v.end(); ++it)
  2. {
  3.  if (*it % 2 == 0)
  4.    it = v.erase(it);
  5.  if (it == v.end())
  6.   break;
  7. }


Título: Re: C++ Error al borrar última casilla de un vector con iteradores
Publicado por: Orubatosu en 3 Diciembre 2014, 21:10 pm
Ahaha... claro como el agua.

Da gusto cuando las cosas al final no solo funcionan, sino que además sabes porque lo hacen  ;-)


Título: Re: C++ Error al borrar última casilla de un vector con iteradores
Publicado por: Eternal Idol en 3 Diciembre 2014, 21:30 pm
Excelente  ::)


Título: Re: C++ Error al borrar última casilla de un vector con iteradores
Publicado por: Orubatosu en 4 Diciembre 2014, 10:04 am
Vale... ahora he entendido realmente porque hay que usar el método de igualar.

Lo pongo para los que se encuentren con este problema, porque el motivo es cuando menos... "curioso" y si a mi me ha pasado, supongo que les pasará a mas personas.

El caso que tenía:

Código:
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;
}

Funciona, pero no es correcto

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

Así es correcto.

El motivo (que tiene cojones) es que el iterador en realidad apunta a una posición de memoria. Esto puede parecer algo irrelevante, ya que para nosotros el iterador apunta a una casilla del vector.

Pero... ah... pero el sistema puede mover el vector de unas posiciones de memoria a otras sin avisarnos para optimizar el uso de la memoria. En operaciones de borrado por lo general no debería de haber problemas, pero cuando insertamos en lugar de borrar, el vector aumenta de tamaño, al aumentar de tamaño ocupa mas memoria y si no puede hacerse mas grande en la posición en la que se encuentra, porque los bloques contiguos están ocupados por otras variables (o por lo que sea), el sistema mueve todo el vector a otras posiciónes de memoria.

Y claro, mueve el vector... pero el iterador se queda "mirando para Cuenca" por decirlo de algún modo. Se queda apuntando a una posición de memoria donde el vector puede ya no estar.

Haciendo estas operaciones como asignaciones, los métodos de insert y erase devuelven un iterador que siempre apunta al vector resultante . Se mueva o no el vector en la memoria.

Y yo que creía que lo había entendido... pero solo a medias  ;D


Título: Re: C++ Error al borrar última casilla de un vector con iteradores
Publicado por: Eternal Idol en 4 Diciembre 2014, 10:16 am
Si, por eso lo de:
Me parece que no hay ninguna garantia de que it continue siendo valido despues de llamar a erase

En el enlace que dejaste al principio lo deja claro:
Iterator validity
Iterators, pointers and references pointing to position (or first) and beyond are invalidated, with all iterators, pointers and references to elements before position (or first) are guaranteed to keep referring to the same elements they were referring to before the call.