Как использовать итератор? - PullRequest
69 голосов
/ 26 апреля 2010

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

186 голосов
/ 26 апреля 2010

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

struct point {
    float x;
    float y;
};

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

20 голосов
/ 26 апреля 2010

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

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

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

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

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 голосов
/ 26 апреля 2010

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

  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)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...