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

Я пытаюсь рассчитать расстояние между двумя точками. Две точки, которые я сохранил в векторе в C++: (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;
}

3 ответа

Решение

То, что ваш код компилируется, возможно потому, что у вас есть 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-измом, ненужным в C++. Просто скажи это

struct point {
    float x;
    float y;
};

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

По совпадению, вы фактически используете встроенную функцию 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) << " ";

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

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

    distance( *ii, *jj)
    

Если вы не измените интерфейс distance() функция, вам, возможно, придется вызвать ее, используя что-то вроде следующего, чтобы получить соответствующие указатели:

distance( &*ii, &*jj)
Другие вопросы по тегам