75 votes

Comment utiliser un itérateur ?

J'essaie de calculer la distance entre deux points. Les deux points sont stockés dans un vecteur en C++ : (0,0) et (1,1).

Je suis censé obtenir des résultats comme

0
1.4
1.4
0

Mais le résultat réel que j'ai obtenu est

0
1
-1
0

Je pense qu'il y a un problème avec la façon dont j'utilise l'itérateur dans le vecteur. Comment puis-je résoudre ce problème ?

J'ai posté le code ci-dessous.

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

204voto

sbi Points 100828

Si votre code compile tout de même, c'est probablement parce que vous avez une using namespace std quelque part. (Sinon vector devrait être std::vector .) C'est quelque chose que je déconseille et vous venez d'en fournir une bonne explication :
Par accident, votre appel est capté std::distance() qui prend deux itérateurs et calcule la distance entre eux. Supprimez la directive using et préfixez tous les types de la bibliothèque standard par std:: et le compilateur vous dira que vous avez essayé de passer un vector <point>::iterator où un point* était nécessaire.

Pour obtenir un pointeur sur un objet vers lequel pointe un itérateur, il faut déréférencer l'itérateur - qui donne une référence à l'objet - et prendre l'adresse du résultat : &*ii .
(Notez qu'un pointeur remplirait parfaitement toutes les conditions pour un std::vector et certaines implémentations antérieures de la bibliothèque standard utilisaient en effet des pointeurs pour cela, ce qui permettait de traiter les itérateurs std::vector itérateurs comme des pointeurs. Mais les implémentations modernes utilisent une classe d'itérateurs spéciale pour cela. Je suppose que la raison en est que l'utilisation d'une classe permet de surcharger les fonctions pour les pointeurs et les itérateurs. De plus, l'utilisation de pointeurs comme std::vector itérateurs encourage le mélange des pointeurs et des itérateurs, ce qui empêchera le code de compiler lorsque vous changerez de conteneur).

Mais plutôt que de faire cela, je vous suggère de modifier votre fonction pour qu'elle prenne des références à la place (cf. cette réponse pour savoir pourquoi c'est une bonne idée de toute façon). :

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

Notez que les points sont pris par const références. Cela indique à l'appelant que la fonction ne modifiera pas les points qui lui sont transmis.

Alors vous pouvez l'appeler comme ceci : distance(*ii,*jj) .


D'ailleurs, cette

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

est un C-isme inutile en C++. Il suffit de l'écrire

struct point {
    float x;
    float y;
};

Cela poserait des problèmes si cette struct jamais été analysée par un compilateur C (le code devrait faire référence à struct point alors, pas seulement point ), mais je suppose que std::vector et autres seraient de toute façon un défi bien plus grand pour un compilateur C.

16 votes

Cette réponse est incorrecte. std::distance peut être récupéré par ADL sur un itérateur std: :, il peut donc faire partie de l'ensemble des candidats indépendamment du fait que std est utilisé.

4 votes

@Puppy : C'est effectivement vrai (et pendant 2 ans et demi personne ne l'a remarqué), mais ce n'est pas tout ce que ma réponse disait. Les points de passage par const point& p1 résoudra également ce problème.

5 votes

@sbi : Non, cela ne résoudra pas le problème. Il sera toujours possible d'écrire par erreur distance(ii, jj) et obtenir std::distance .

21voto

Joris Timmermans Points 8075

Par coïncidence, tu es en train d'utiliser une fonction STL intégrée "distance". qui calcule la distance entre les itérateurs, au lieu d'appeler votre propre fonction de distance. Vous devez "déréférencer" vos itérateurs pour obtenir l'objet contenu.

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

Comme vous pouvez le voir dans la syntaxe ci-dessus, un "itérateur" ressemble beaucoup à un "pointeur" généralisé. L'itérateur ne peut pas être utilisé directement comme "votre" type d'objet. En fait, les itérateurs sont tellement similaires aux pointeurs que de nombreux algorithmes standard qui opèrent sur des itérateurs fonctionnent bien sur des pointeurs également.

Comme l'a noté Sbi : votre fonction de distance prend des pointeurs. Il serait préférable de la réécrire en prenant des références constantes à la place, ce qui rendrait la fonction plus "canonique" en c++, et rendrait la syntaxe de déréférencement des itérateurs moins pénible.

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

6voto

Michael Burr Points 181287

Vous pouvez faire plusieurs choses :

  1. Faites le distance() prennent des références à point objets. Il s'agit simplement de rendre les choses plus lisibles lors de l'appel de la fonction distance() fonction :

    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));
    }
  2. Déréférencez vos itérateurs lorsque vous appelez distance() donc vous passez le point objets :

    distance( *ii, *jj)

    Si vous ne changez pas l'interface de la distance() fonction, vous pourriez l'appeler en utilisant quelque chose comme ce qui suit pour obtenir les appropriés :

    distance( &*ii, &*jj)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X