que programacion para listas libreria iteradores iterador contenedoras clases c++ pointers vector iterator using-statement

programacion - libreria para listas c++



¿Cómo usar un iterador? (3)

Estoy tratando de calcular la distancia entre dos puntos. Los dos puntos que almacené en un vector en C ++: (0,0) y (1,1).

Se supone que debo obtener resultados como

0 1.4 1.4 0

Pero el resultado real que obtuve es

0 1 -1 0

Creo que hay algo mal con la forma en que uso el iterador en el vector. ¿Como puedo solucionar este problema?

Publiqué el código a continuación.

typedef struct point { float x; float y; } point; float distance(point *p1, point *p2) { return sqrt((p1->x - p2->x)*(p1->x - p2->x) + (p1->y - p2->y)*(p1->y - p2->y)); } int main() { vector <point> po; point p1; p1.x = 0; p1.y = 0; point p2; p2.x = 1; p2.y = 1; po.push_back(p1); po.push_back(p2); vector <point>::iterator ii; vector <point>::iterator jj; for (ii = po.begin(); ii != po.end(); ii++) { for (jj = po.begin(); jj != po.end(); jj++) { cout << distance(ii,jj) << " "; } } return 0; }


Por coincidencia, en realidad está usando una función de "distancia" de AWL incorporada , que calcula la distancia entre iteradores, en lugar de llamar a su propia función de distancia. Necesita "eliminar la referencia" de sus iteradores para obtener el objeto contenido.

cout << distance(&(*ii), &(*jj)) << " ";

Como puede ver en la sintaxis anterior, un "iterador" es bastante parecido a un "puntero" generalizado. El iterador no se puede usar directamente como "su" tipo de objeto. De hecho, los iteradores son tan similares a los punteros que muchos algoritmos estándar que operan en iteradores también funcionan bien en los punteros.

Como observó Sbi: su función de distancia toma punteros. Sería mejor reescribir que tomando referencias de referencias en su lugar, lo que haría que la función sea más "canónica" c ++, y hacer que la sintaxis de desreferencia del iterador sea menos dolorosa.

float distance(const point& i_p1, const point& i_p2) { return sqrt((p1.x - p2.x)*(p1.x - p2.x) + (p1.y - p2.y)*(p1.y - p2.y)); } cout << distance(*ii, *jj) << " ";


Puede hacer un par de cosas:

  1. Haga que la función distance() tome referencias a objetos point . Esto es realmente solo para hacer las cosas más legibles cuando se llama a la función distance() :

    float distance(point const& p1, point const& p2) { return sqrt((p1.x - p2.x)*(p1.x - p2.x) + (p1.y - p2.y)*(p1.y - p2.y)); }

  2. Desconéctese de sus iteradores al llamar a distance() para que pase los objetos point :

    distance( *ii, *jj)

Si no cambia la interfaz de la función distance() , es posible que deba llamarla usando algo como lo siguiente para obtener los punteros apropiados:

distance( &*ii, &*jj)


Que el código se compila en absoluto es probablemente porque tiene un using namespace std alguna parte. (De lo contrario, vector tendría que ser std::vector .) Eso es algo que desaconsejaría y acaba de proporcionar un buen caso por qué:
Por accidente, su llamada recoge std::distance() , que toma dos iteradores y calcula la distancia entre ellos. Elimine la directiva using y prefija todos los tipos de biblioteca estándar con std:: y el compilador le dirá que intentó pasar un vector <point>::iterator donde se requirió un point* .

Para obtener un puntero a un objeto al que apunta un iterador, debe eliminar la referencia del iterador, que proporciona una referencia al objeto, y tomar la dirección del resultado: &*ii .
(Tenga en cuenta que un puntero cumpliría perfectamente todos los requisitos para un iterador std::vector y algunas implementaciones anteriores de la biblioteca estándar sí utilizaron punteros para eso, lo que le permitió tratar los iteradores std::vector como punteros. Pero las implementaciones modernas usan un especial clase de iterador para eso. Supongo que la razón es que usar una clase permite funciones de sobrecarga para punteros e iteradores. Además, usar punteros como std::vector iteradores std::vector estimula la mezcla de punteros e iteradores, lo que evitará que el código se compile cuando cambie su contenedor .)

Pero en lugar de hacer esto, le sugiero que cambie su función para que tome referencias en su lugar (consulte esta respuesta para saber por qué esa es una buena idea de todos modos):

float distance(const point& p1, const point& p2) { return sqrt((p1.x - p2.x)*(p1.x - p2.x) + (p1.y - p2.y)*(p1.y - p2.y)); }

Tenga en cuenta que los puntos son tomados por referencias de const . Esto indica a la persona que llama que la función no cambiará los puntos en que se pasa.

Entonces puedes llamarlo así: distance(*ii,*jj) .

En una nota lateral, esto

typedef struct point { float x; float y; } point;

es un C-ismo innecesario en C ++. Solo deletrearlo

struct point { float x; float y; };

Eso causaría problemas si esta definición de struct alguna vez fuera analizar desde un compilador de C (el código tendría que referirse al struct point entonces, no simplemente al point ), pero supongo que std::vector y similares serían mucho más desafiantes a un compilador de C de todos modos.