Какой лучший способ написать итераторы для цикла в C ++ - PullRequest
5 голосов
/ 13 июля 2011

Для очень простой вещи, например, для печати каждого элемента в векторе, какой лучший способ использовать в C ++?

Я использовал это:

for (vector<int>::iterator i = values.begin(); i != values.end(); ++i)

раньше, но в одном из примеров Boost :: filesystem я видел это так:

for (vec::const_iterator it(v.begin()), it_end(v.end()); it != it_end; ++it)

Для меня это выглядит сложнее, и я не понимаю, почему это лучше, чем тот, который я использовал.

Можете ли вы сказать мне, почему эта версия лучше? Или это не имеет значения для простых вещей, таких как печать элементов вектора?

i != values.end() замедляет итерацию?

Или это const_iterator против iterator? Const_iterator быстрее в цикле, как это?

Ответы [ 5 ]

10 голосов
/ 13 июля 2011
  1. Foo x = y; и Foo x(y); эквивалентны, поэтому используйте любой, который вы предпочитаете.

  2. Вывод end из цикла может или не может быть чем-то, что компилятор будет делать в любом случае, в любом случае это делает явным, что конец контейнера не меняется.

  3. Используйте const-итераторы, если вы не собираетесь изменять элементы, потому что это то, что они имеют в виду.

for (MyVec::const_iterator it = v.begin(), end = v.end(); it != end; ++it)
{
  /* ... */
}

В C ++ 0x используйте auto + cbegin():

for (auto it = v.cbegin(), end = v.cend(); it != end; ++it)

(Возможно, вы захотите использовать готовый контейнер с симпатичным принтером ?)

6 голосов
/ 13 июля 2011
for (vector<int>::iterator i = values.begin(); i != values.end(); ++i)

... vs ...

for (vec::const_iterator it(v.begin()), it_end(v.end()); it != it_end; ++it)

Для меня [последнее видно в надстроек] выглядит сложнее, и я не понимаю, почему это лучше, чемодин я использовал.

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

Можете ли вы сказать мне, почему эта версия лучше?Или это не имеет значения для простых вещей, таких как печать элементов вектора?I! = Values.end () замедляет итерацию?

  • it_end

    • Производительность : it_end получает значение end() только один раз, какначало цикла.Для любого контейнера, где вычисление end() было слишком дорогим, его вызов только один раз может сэкономить процессорное время.Для любой наполовину достойной реальной библиотеки C ++ Standard все функции end() не выполняют вычислений и могут быть встроены для эквивалентной производительности.На практике, если нет некоторого шанса, что вам может понадобиться добавить нестандартный контейнер с более дорогой функцией end(), нет смысла явно «кэшировать» end() в оптимизированном коде.

      ЭтоИнтересно, поскольку для vector это означает, что size() может потребовать небольшого вычисления - концептуально вычесть begin() из end(), а затем разделить на sizeof(value_type) (компиляторы масштабируются по размеру неявно во время арифметики с указателями), например, GCC 4.5.2:

      size_type size() const
      { return size_type(this->_M_impl._M_finish - this->_M_impl._M_start); }

    • Обслуживание : если код эволюционирует для вставки или удаления элементов внутри цикла (очевидно, что итератор сам по себе не признан недействительным - вероятным для карт / наборов / списков и т. д.) это еще одна точка обслуживания (и, следовательно, вероятность ошибок), если кешированное значение end() также необходимо явно пересчитать.

  • Небольшая деталь, но здесь vec должно быть typedef, и IMHO часто лучше использовать typedef для контейнеров, так какослабляетСоединение контейнерного типа с доступом к типам итераторов.

  • type identifier(expr)

    • Стиль и документальный акцент :type identifier(expr) более точно указывает на вызов конструктора, чем type identifier = expr, что является основной причиной, по которой некоторые люди предпочитают форму.Я обычно предпочитаю последнее, поскольку мне нравится подчеркивать смысл присваивания ... это визуально однозначно, тогда как нотация вызова функции используется для многих вещей.

    • Почти эквивалентность: Для большинства классов оба в любом случае вызывают один и тот же конструктор, , но , если type имеет явный конструктор типа expr, он будет передан, если = используется.Хуже того, некоторые другие преобразования могут позволить использовать менее идеальный конструктор.Например, X x = 3.14; будет проходить через explicit X::X(double);, чтобы соответствовать X::X(int) - вы можете получить менее точный (или просто неверный) результат - но я еще не укушен такой проблемой, так что это довольно теоретически!

Или это const_iterator против итератора?Является ли const_iterator быстрее в цикле, подобном этому?

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

Учитывая, что C ++ 0x упоминается в других ответах - но только дополнительное преимуществоauto и cbegin / cend - также поддерживается новая запись:

for (const Foo& foo: container)
    // use foo...
3 голосов
/ 13 июля 2011

Чтобы напечатать элементы в векторе, вы не должны использовать ничего из вышеперечисленного (по крайней мере, IMO).

Я бы порекомендовал что-то вроде этого:

std::copy(values.begin(), values.end(), 
          std::ostream_iterator<T>(std::cout, "\n"));
0 голосов
/ 13 июля 2011

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

for (vector<int>::iterator i = values.begin(); i != values.end(); ++i)
0 голосов
/ 13 июля 2011

Вы можете просто получить к ним доступ по индексу

int main(int argc, char* argv[])
{
    std::vector<int> test;

    test.push_back(10);
    test.push_back(11);
    test.push_back(12);

    for(int i = 0; i < test.size(); i++)
        printf("%d\n", test[i]);
}

распечатывает: 10 11 12

...