2010-04-26 18 views
61

Sto provando a calcolare la distanza tra due punti. I due punti che ho memorizzato in un vettore in C++: (0,0) e (1,1).Come utilizzare un iteratore?

Dovrei ottenere risultati come

0 
1.4 
1.4 
0 

Ma il risultato effettivo che ho ottenuto è

0 
1 
-1 
0 

Penso che ci sia qualcosa di sbagliato nel modo in cui io uso iteratore nel vettore. Come posso risolvere questo problema?

Ho pubblicato il codice qui sotto.

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

risposta

158

che il codice viene compilato a tutti è probabilmente perché avete un using namespace std da qualche parte. (Altrimenti) That's something I would advise against e hai appena fornito una buona causa perché:
Per errore, la tua chiamata riprende std::distance(), che accetta due iteratori e calcola la distanza tra loro. Rimuovi la direttiva using e prefissa tutti i tipi di libreria standard con std:: e il compilatore ti dirà che hai provato a passare uno vector <point>::iterator dove era richiesto un point*.

Per ottenere un puntatore a un oggetto a cui punta un iteratore, è necessario effettuare il dereferenziamento dell'iteratore, che fornisce un riferimento all'oggetto, e prendere l'indirizzo del risultato: &*ii.
(Si noti che un puntatore soddisfa perfettamente tutti i requisiti per un iteratore std::vector e alcune implementazioni precedenti della libreria standard infatti utilizzavano puntatori per questo, che permettevano di trattare gli iteratori std::vector come puntatori, ma le moderne implementazioni usano una classe di iteratore speciale per quello . suppongo che il motivo è che l'utilizzo di una classe permette funzioni per i puntatori e gli iteratori sovraccarico. Inoltre, utilizzando i puntatori come std::vector iteratori incoraggia la miscelazione puntatori e iteratori, che impediranno il codice per compilare quando si cambia il vostro contenitore.)

Ma piuttosto che farlo, ti suggerisco di cambiare la tua funzione in modo che richieda riferimenti (vedi this answer perché è comunque una buona idea.):

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)); 
} 

Si noti che i punti sono presi dai riferimenti const. Questo indica al chiamante che la funzione non cambierà i punti che è passato.

Quindi è possibile chiamarlo in questo modo: distance(*ii,*jj).


Una nota a parte, questo

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

è un C-ism non necessarie in C++. Basta scriverlo

struct point { 
    float x; 
    float y; 
}; 

che farebbe problemi se questa definizione struct mai stato quello di analizzare da un compilatore C (il codice avrebbe dovuto fare riferimento a struct point poi, non semplicemente point), ma credo che std::vector e simili sarebbe essere molto più di una sfida per un compilatore C comunque.

+10

Questa risposta è errata. std :: distance può essere raccolto da ADL su un std :: iterator, quindi può far parte del set candidato indipendentemente dal fatto che sia o meno usato 'std'. – Puppy

+2

@Puppy: Questo è vero (e per 2,5 anni nessuno se n'è accorto), ma non è tutta la mia risposta. Passare punti per 'const point & p1' risolverà anche questo problema. – sbi

+3

@sbi: No, non risolverà il problema. Sarà ancora possibile scrivere erroneamente 'distance (ii, jj)' e ottenere 'std :: distance'. –

17

Per pura coincidenza, si sta usando a built-in STL function "distance", che calcola la distanza tra iteratori, invece di chiamare la propria funzione di distanza. Hai bisogno di "dereferenziare" i tuoi iteratori per ottenere l'oggetto contenuto.

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

Come si può vedere dalla sintassi di cui sopra, un "iteratore" è un bel po 'come un "puntatore" generalizzato. L'iteratore non può essere utilizzato direttamente come "tuo" tipo di oggetto. In effetti gli iteratori sono così simili ai puntatori che molti algoritmi standard che operano sugli iteratori funzionano bene anche sui puntatori.

Come indicato da Sbi: la funzione distanza prende i puntatori. Sarebbe meglio riscriverlo come prendendo invece riferimenti const, il che renderebbe la funzione più "canonica" C++ e renderebbe meno dolorosa la sintassi di dereference dell'iteratore.

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

Si potrebbe fare un paio di cose:

  1. rendere la funzione distance() prendere riferimenti a point oggetti. Questo è in realtà solo per rendere le cose più leggibile quando si chiama la funzione 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. risoluzione del riferimento vostri iteratori al momento della chiamata distance() così si sta passando gli oggetti point:

    distance(*ii, *jj) 
    

Se non modificare l'interfaccia della funzione distance(), potrebbe essere necessario chiamarla utilizzando qualcosa di simile al seguente per ottenere i puntatori appropriati:

distance(&*ii, &*jj) 
Problemi correlati