Подтвердить что ты не робот

Итераторы.. зачем их использовать?

В библиотеке STL в некоторых контейнерах есть итераторы, и обычно считается, что они являются превосходным способом итерации через эти контейнеры, а не просто для циклов, например.

for ( int i=0; i < vecVector.size(); i++ )
{

..

}

Может кто-нибудь сказать мне, почему и в каких случаях я должен использовать итераторы и в каких случаях фрагмент кода выше, пожалуйста?

4b9b3361

Ответ 1

Обратите внимание, что обычно реализация вектора не будет использовать "int" в качестве типа индекса/размера. Таким образом, ваш код будет, по крайней мере, спровоцировать предупреждения компилятора.

Генеричность

Итераторы увеличивают общую целостность вашего кода.

Например:

typedef std::vector<int> Container ;

void doSomething(Container & p_aC)
{
    for(Container::iterator it = p_aC.begin(), itEnd = p_aC.end(); it != itEnd; ++it)
    {
       int & i = *it ; // i is now a reference to the value iterated
       // do something with "i"
    }
}

Теперь предположим, что вы меняете вектор в список (потому что в вашем случае список теперь лучше). Вам нужно только изменить объявление typedef и перекомпилировать код.

Если вы использовали код на основе индекса, его нужно было бы переписать.

Доступ

Итератор следует рассматривать как своего рода супер указатель. Он "указывает" на значение (или, в случае карт, на пару ключей/значений).

Но у него есть методы для перехода к следующему элементу в контейнере. Или предыдущий. Некоторые контейнеры предлагают даже произвольный доступ (вектор и дека).

Алгоритмы

Большинство алгоритмов STL работают на итераторах или на диапазонах итераторов (опять же, из-за общности). Здесь вы не сможете использовать индекс.

Ответ 2

Использование итераторов позволяет вашему коду быть агностиком в отношении реализации вашего контейнера. Если случайный доступ для вашего контейнера дешев, разница в производительности невелика.

Но во многих случаях вы не будете знать, так ли это. Если вы попытаетесь использовать свой метод в связанном списке, например, с подпиской, контейнер должен будет ходить по списку на каждой итерации, чтобы найти ваш элемент.

Итак, если вы точно не знаете, что случайный доступ к вашему контейнеру дешев, используйте итератор.

Ответ 3

Если вы используете итераторы в качестве аргументов вашей функции, вы можете отделить ее от используемого типа "контейнер". Например, вы можете направить результаты функции на консольный вывод, а не на вектор (пример ниже). Этот трюк может быть чрезвычайно мощным для уменьшения связи между вашими классами. Легко связанные классы намного легче тестировать.

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

template <typename InputIterator, typename OutputIterator>
void AddOne(InputIterator begin, InputIterator end, OutputIterator dest)
{
    while (begin != end)
    {
        *dest = *begin + 1;
        ++dest;
        ++begin;
    }
}

int _tmain(int argc, _TCHAR* argv[])
{
    vector<int> data;
    data.push_back(1);
    data.push_back(2);
    data.push_back(3);

    // Compute intermediate results vector and dump to console
    vector<int> results;
    AddOne(data.begin(), data.end(), back_inserter(results));
    copy(results.begin(), results.end(), ostream_iterator<int>(cout, " "));
    cout << endl;

    // Compute results and send directly to console, no intermediate vector required
    AddOne(data.begin(), data.end(), ostream_iterator<int>(cout, " "));
    cout << endl;

    return 0;
}

Ответ 4

В вашем примере вызов vecVector.size() менее эффективен, чем использование итератора. Итератор, по сути, инкапсулирует вас от необходимости беспокоиться о размере повторного контейнера. Кроме того, итератору необязательно идти в последовательном порядке. Он просто должен отвечать на вызов .next любым способом, который он считает нужным.

Ответ 5

Ну, с одной стороны, вышеуказанное больше не будет работать, если вы превратите этот вектор в список.

Итераторы позволяют создавать шаблоны функций, которым не нужно знать тип контейнера, над которым они работают. Вы даже можете сделать следующее:

#include <algorithm>

void printvalue(double s)
{
    // Do something with s
}

int _tmain(int argc, _TCHAR* argv[])
{
    double s[20] = {0};

    std::for_each(s, s+20, printvalue);

    return 0;
}

Это потому, что стандартный указатель также является допустимым итератором для for_each.

Dave

Ответ 6

Итератор в основном представляет собой более высокий уровень абстракции.

В вашем фрагменте предполагается, что контейнер может быть проиндексирован. Это справедливо для std::vector<> и некоторых других контейнеров, например необработанных массивов.

Но std::set<> полностью не индексируется, а оператор индекса std::map<> будет вставлять в него любой аргумент, а не ожидаемое поведение вашего for -loop.

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