2010-04-26 17 views
61

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

supone que tengo que obtener resultados como

0 
1.4 
1.4 
0 

Pero el resultado real que lo que tengo es

0 
1 
-1 
0 

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

He publicado 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; 
} 

Respuesta

158

que su código se compila en absoluto es probablemente porque tiene un using namespace std en alguna parte. (De lo contrario, vector tendría que ser std::vector) That's something I would advise against y acaba de proporcionar un buen caso por qué:
Por accidente, su llamada retoma std::distance(), que toma dos iteradores y calcula la distancia entre ellos. Elimine la directiva de uso 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ó 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 utilizaban punteros para eso, lo que le permitía tratar los iteradores std::vector como punteros. Pero las implementaciones modernas usan una clase de iterador especial para ese Supongo que la razón es que usar una clase permite funciones de sobrecarga para punteros e iteradores. Además, usar punteros como iteradores std::vector alienta a mezclar punteros e iteradores, lo que evitará que el código 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 (vea this answer para saber por qué 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 se toman por referencias const. Esto indica a la persona que llama que la función no cambiará los puntos en que se pasa.

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


En una nota lateral, esta

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

es un C-ISM innecesario en C++. Sólo deletrearlo

struct point { 
    float x; 
    float y; 
}; 

que haría problemas si esta definición struct vez hubo que analizar desde un compilador de C (el código tendría que referirse a struct point entonces, no simplemente point), pero supongo std::vector y similares haría ser mucho más un desafío para un compilador de C de todos modos.

+10

Esta respuesta es incorrecta. std :: distance puede ser recogido por ADL en un std :: iterator, por lo que puede formar parte del conjunto de candidatos independientemente de si se utiliza o no 'std'. – Puppy

+2

@Puppy: Eso es cierto (y durante 2.5 años nadie lo notó), pero esto no es todo lo que dijo mi respuesta. Pasar puntos por 'const point & p1' también resolverá este problema. – sbi

+3

@sbi: No, no resolverá el problema. Todavía será posible escribir erróneamente 'distance (ii, jj)' y obtener 'std :: distance'. –

17

Por coincidencia, en realidad estás usando a built-in STL function "distance", que calcula la distancia entre los 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 se puede ver en la sintaxis anterior, un "repetidor" es mucho como 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 señaló 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) << " "; 
6

Es posible hacer un par de cosas:

  1. hacer que la función distance() tomar referencias a objetos point. Esto es realmente sólo para hacer las cosas más legible 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. Desreferencia sus iteradores al llamar distance() por lo que está pasando los objetos point:

    distance(*ii, *jj) 
    

Si no cambie 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) 
Cuestiones relacionadas