Как использовать итератор?

Я пытаюсь рассчитать расстояние между двумя точками. Две точки, которые я сохранил в векторе в С++: (0,0) и (1,1).

Я должен получить результаты как

0
1.4
1.4
0

Но фактический результат, который я получил, -

0
1
-1
0

Я думаю, что что-то не так с тем, как я использую итератор в векторе. Как я могу исправить эту проблему?

Я разместил код ниже.

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

Ответ 1

Что ваш код компилируется вообще, вероятно, потому, что у вас есть using namespace std где-то. (В противном случае vector должно быть std::vector.) Что-то, что я бы посоветовал против, и вы только что дали хороший аргумент, почему:
Случайно ваш звонок поднимает std::distance(), который принимает два итератора и вычисляет расстояние между ними. Удалите директиву using и префикс всех стандартных типов библиотек с помощью std::, и компилятор скажет вам, что вы пытались передать vector <point>::iterator, где требуется point*.

Чтобы получить указатель на объект, на который указывает итератор, вам придется разыменовать итератор, который дает ссылку на объект, и взять адрес результата: &*ii.
(Обратите внимание, что указатель прекрасно выполнил бы все требования для итератора std::vector, а некоторые более ранние реализации стандартной библиотеки действительно использовали указатели для этого, что позволило вам рассматривать итераторы std::vector как указатели. Но в современных реализациях используется специальный класс итератора Я полагаю, причина в том, что использование класса позволяет перегружать функции для указателей и итераторов. Кроме того, использование указателей в качестве std::vector итераторов поощряет смешивающие указатели и итераторы, что предотвратит компиляцию кода при изменении контейнера.)

Но вместо этого я предлагаю вам изменить свою функцию, чтобы вместо этого ссылаться на ссылки (см. этот ответ, почему это хорошая идея.):

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

Обратите внимание, что точки берутся с помощью const ссылок. Это указывает вызывающему, что функция не изменит переданные точки.

Затем вы можете вызвать его так: distance(*ii,*jj).


С одной стороны, это

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

является C-ism ненужным в С++. Просто заклинание

struct point {
    float x;
    float y;
};

Это создаст проблемы, если это определение struct когда-либо должно было анализировать из компилятора C (код должен был бы ссылаться на struct point, а не просто point), но я думаю, std::vector и тому подобное было бы намного сложнее для компилятора C в любом случае.

Ответ 2

По стечению обстоятельств вы фактически используете встроенную функцию STL "расстояние", которая вычисляет расстояние между итераторами вместо вызова собственной функции расстояния. Вам нужно "разыменовать" свои итераторы, чтобы получить содержащийся объект.

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

Как видно из приведенного выше синтаксиса, "итератор" во многом похож на обобщенный "указатель". Итератор не может быть использован как "ваш" тип объекта напрямую. На самом деле, итераторы настолько похожи на указатели, что многие стандартные алгоритмы, работающие с итераторами, прекрасно работают и с указателями.

Как отметил Sbi: ваша функция расстояния использует указатели. Лучше было бы вместо этого переписать константные ссылки, что сделало бы функцию более "канонической" c++ и сделало бы синтаксис разыменования итератора менее болезненным.

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

Ответ 3

Вы можете сделать пару вещей:

  1. Заставьте функцию distance() принимать ссылки на объекты point. Это действительно просто для того, чтобы сделать вещи более читабельными при вызове функции distance():
    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. Разыменовывайте итераторы при вызове distance(), чтобы передать объекты point:
    distance( *ii, *jj)
    
    Если вы не измените интерфейс функции distance(), вы можете нужно вызвать его, используя что-то вроде следующего, чтобы получить соответствующий указатели:
    distance( &*ii, &*jj)