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

Для вектора, почему предпочитаете итератор над указателем?

В Herb Sutter When Is a Container Not a Container? он показывает пример ввода указателя в контейнер:

  // Example 1: Is this code valid? safe? good?
  //
  vector<char> v;

  // ...

  char* p = &v[0];

  // ... do something with *p ...

Затем следует за ним "улучшение":

  // Example 1(b): An improvement
  //               (when it possible)
  //
  vector<char> v;

  // ...

  vector<char>::iterator i = v.begin();

  // ... do something with *i ...

Но не дает убедительного аргумента:

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

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

  • Вы не всегда можете удобно использовать итератор, где вы можете использовать указатель. (См. Пример ниже.)

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

В случае вектора итератор является просто случайным идентификатором. Для всех целей и целей это тонкая оболочка над указателем. Одна реализация даже подтверждает это:

   // This iterator adapter is 'normal' in the sense that it does not
   // change the semantics of any of the operators of its iterator
   // parameter.  Its primary purpose is to convert an iterator that is
   // not a class, e.g. a pointer, into an iterator that is a class.
   // The _Container parameter exists solely so that different containers
   // using this template can instantiate different types, even if the
   // _Iterator parameter is the same.

Кроме того, реализация сохраняет значение члена типа _Iterator, которое равно pointer или T*. Другими словами, просто указатель. Кроме того, difference_type для такого типа std::ptrdiff_t, а определенные операции - только тонкие обертки (т.е. operator++ - ++_pointer, operator* - *_pointer) и т.д.

После аргумента Sutter этот класс итератора не дает преимуществ по сравнению с указателями, а только недостатками. Правильно ли я?

4b9b3361

Ответ 1

Для векторов в нестандартном коде вы в основном правы.

Преимущество состоит в том, что вы можете передать RandomAccessIterator целый набор алгоритмов независимо от того, в каком контейнере итератор выполняет итерацию, независимо от того, имеет ли этот контейнер непрерывное хранилище (и, следовательно, итераторы указателя) или нет. Это абстракция.

(Эта абстракция, помимо прочего, позволяет реализациям заменять базовую реализацию указателя на что-то более сексуальное, например, итераторы с проверкой диапазона для использования отладки.)

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

Итераторы также самодокументируются таким образом, что указатели не являются. Что означает int*? Без понятия. Что означает std::vector<int>::iterator? Aha & hellip;

Наконец, они обеспечивают меру безопасности типа; хотя такие итераторы могут быть только тонкими обертками вокруг указателей, они не обязательно должны быть указателями: если итератор является отдельным типом, а не псевдонимом типа, то вы не будете случайно передавать свой итератор в места, которые вы не хотели бы перейдите или установите его в "NULL" случайно.

Я согласен, что аргумент Саттера примерно такой же убедительный, как и большинство его других аргументов, т.е. не очень.

Ответ 2

Одна из реальных причин предпочесть итераторы над указателями состоит в том, что они могут быть реализованы как проверены итераторы в отладочных сборках и помогут вам поймать некоторые неприятные проблемы на раннем этапе. То есть:

vector<int>::iterator it; // uninitialized iterator
it++;

или

for (it = vec1.begin(); it != vec2.end(); ++it) // different containers

Ответ 3

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

Это не один из недостатков. Иногда просто "удобно", чтобы передать указатель в те места, где вы действительно не хотели, чтобы они уходили. Наличие отдельного типа помогает в проверке параметров.

В некоторых ранних реализациях использовался T* для vector:: iterator, но он вызывал различные проблемы, например, люди, случайно передающие несвязанный указатель на функции векторных членов. Или назначить NULL итератору.

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

Это было написано в 1999 году, когда мы также полагали, что код в <algorithm> должен быть оптимизирован для разных типов контейнеров. Не намного позже все были удивлены, увидев, что компиляторы поняли это сами. Общие алгоритмы, использующие итераторы, работали отлично!

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