web-dev-qa-db-fra.com

Comment utiliser un itérateur?

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

Je suis censé obtenir des résultats

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

Que votre code compile du tout est probablement parce que vous avez un using namespace std Quelque part. (Sinon, vector devrait être std::vector.) C'est quelque chose que je déconseille = et vous venez de fournir un bon cas pourquoi:
Par accident, votre appel décroche std::distance(), qui prend deux itérateurs et calcule la distance entre eux. Supprimez la directive using et préfixez tous les types de bibliothèques standard avec std:: Et le compilateur vous dira que vous avez essayé de passer un vector <point>::iterator Où un point* Était requis.

Pour obtenir un pointeur sur un objet vers lequel pointe un itérateur, vous devez 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 satisferait parfaitement toutes les exigences pour un itérateur std::vector Et certaines implémentations antérieures de la bibliothèque standard utilisaient en effet des pointeurs pour cela, ce qui vous permettait de traiter std::vector Itérateurs comme pointeurs. Mais les implémentations modernes utilisent une classe d'itérateurs spéciale pour cela. Je suppose que la raison est que l'utilisation d'une classe permet de surcharger les fonctions pour les pointeurs et les itérateurs. De plus, l'utilisation des pointeurs comme std::vector itérateurs encourage le mélange des pointeurs et des itérateurs, empêchera le code de se compiler lorsque vous modifiez votre conteneur.)

Mais plutôt que de faire cela, je vous suggère de changer votre fonction afin qu'elle prenne des références à la place (voir cette réponse pour 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 des références const. Cela indique à l'appelant que la fonction ne changera pas les points qu'elle a passés.

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


En passant, cette

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

est un C-ism inutile en C++. Il suffit de l'épeler

struct point {
    float x;
    float y;
};

Cela poserait des problèmes si cette définition struct devait jamais être analysée à partir d'un compilateur C (le code devrait alors faire référence à struct point, Pas simplement point), mais je suppose std::vector Et autres seraient de toute façon bien plus un défi pour un compilateur C.

188
sbi

Par coïncidence, vous utilisez en fait ne 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 si similaires aux pointeurs que de nombreux algorithmes standard qui fonctionnent sur les itérateurs fonctionnent également très bien sur les pointeurs.

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

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) << " ";
21
Joris Timmermans

Vous pourriez faire deux ou trois choses:

  1. Faites en sorte que la fonction distance() prenne des références aux objets point. C'est vraiment juste pour rendre les choses plus lisibles lors de l'appel de la fonction 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. Déréférencer vos itérateurs lors de l'appel de distance() afin de passer les objets point:

    distance( *ii, *jj)
    

Si vous ne modifiez pas l'interface de la fonction distance(), vous devrez peut-être l'appeler en utilisant quelque chose comme le suivant pour obtenir les pointeurs appropriés:

distance( &*ii, &*jj)
6
Michael Burr