Проверка правильности итератора

Есть ли способ проверить, является ли итератор (будь то из вектора, списка, deque...) (по-прежнему), с возможностью перенаправления, т.е. не был признан недействительным?

Я использовал try - catch, но есть ли более прямой способ сделать это?

Пример: (который не работает)

list<int> l;
for (i = 1; i<10; i++) {
    l.push_back(i * 10);
}

itd = l.begin();
itd++;
if (something) {
    l.erase(itd);
}

/* now, in other place.. check if itd points to somewhere meaningful */
if (itd != l.end())
{
    //  blablabla
}

Ответ 1

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

Ответ 2

Как сказал jdehaan, если итератор не был недействителен и указывает на контейнер, вы можете проверить, сравнив его с container.end().

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

std::vector<int>::iterator iter = vec.begin();
vec.resize(vec.capacity() + 1);
// iter is now singular, you may only perform assignment on it,
// there is no way in general to determine whether it is singular or not

Ответ 3

Обычно вы проверяете его, проверяя, отличается ли он от конца(), например

if (it != container.end())
{
   // then dereference
}

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

Ответ 4

Непортативный ответ: Да - в Visual Studio

Итераторы Visual Studio STL имеют режим "отладки", который делает именно это. Вы не хотели бы включать это в сборку корабля (есть накладные расходы), но полезны в проверенных сборках.

Прочитайте об этом на VC10 здесь (эта система может и фактически меняет каждый выпуск, поэтому найдите документы, специфичные для вашей версии).

Изменить. Кроме того, я должен добавить: итераторы отладки в visual studio предназначены для немедленного взрыва при их использовании (вместо undefined поведения); не позволять "запрашивать" их состояние.

Ответ 5

Попытка и ловли небезопасны, вы не будете или, по крайней мере, редко бросаете, если ваш итератор "вне пределов".

что, как сказал alemjerus, итератор всегда может быть разыменован. Независимо от того, что уродство лежит ниже. Вполне возможно, итерация в другие области памяти и запись в другие области, которые могут содержать другие объекты. Я смотрю на код, наблюдая переменные изменения без особых причин. Это ошибка, которую трудно обнаружить.

Также разумно помнить, что вставка и удаление элементов может потенциально привести к недействительности ссылок all, указателей и итераторов.

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

Ответ 6

Есть ли способ проверить, является ли итератор (будь то из вектора, списка, deque...) (по-прежнему) отменяемым, т.е. не был аннулирован?

Нет, нет. Вместо этого вам нужно контролировать доступ к контейнеру, пока существует ваш итератор, например:

  • Ваш поток не должен изменять контейнер (аннулировать итератор), пока он все еще использует экземпляр-итератор для этого контейнера

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

Рабочие элементы, такие как перехват исключения, не будут работать.

Это конкретный пример более общей проблемы: "Могу ли я проверить/определить, является ли указатель действительным?", ответ на который обычно "нет", вы не можете проверить его: вместо этого вам нужно управлять все распределения и удаления памяти, чтобы узнать, все ли по-прежнему действителен данный указатель ".

Ответ 7

if (iterator != container.end()) {
   iterator is dereferencable !
}

Если ваш итератор не равен container.end(), и он не является разыменованным, вы делаете что-то неправильно.

Ответ 8

В некоторых контейнерах STL текущий итератор становится недействительным, когда вы удаляете текущее значение итератора. Это происходит потому, что операция стирания изменяет структуру внутренней памяти контейнера, а оператор increment на существующем итераторе указывает на расположение undefined.

Когда вы выполняете следующее, итератор вставляется, прежде чем он будет передан для удаления функции.

if (something) l.erase(itd++);

Ответ 9

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

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

Каждый итератор, который указывал на какой-либо элемент, который был удален из контейнера, становится недействительным, но он не пропускает конец контейнера!

Это означает, что итератор, который указывал на какой-либо элемент, который был удален из контейнера, не может сравниться с container.end(). Этот итератор недействителен, и поэтому он не является разуменяемым, т.е. Вы не можете использовать ни операторы * nor → , но также не увеличивать, т.е. Вы не можете использовать оператор ++, а также не уменьшаться, т.е. Вы не можете использовать оператор.

Это тоже не сопоставимо!!! И.Е. вы даже не можете использовать ни == nor!= operator

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

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

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

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

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

bool IsNull(list<int>::iterator& i) //In your example, you have used list<int>, but if your container is not list, then you have to change this parameter to the type of the container you are using, if it is either a vector or deque, and also the type of the element inside the container if necessary.
{
    byte buffer[sizeof(i)];
    memset(buffer, 0, sizeof(i));
    memcpy(buffer, &i, sizeof(i));
    return *buffer == 0; //I found that the size of any iterator is 12 bytes long. I also found that if the first byte of the iterator that I copy to the buffer is zero, then the iterator is invalid. Otherwise it is valid. I like to call invalid iterators also as "null iterators".
}

Я уже тестировал эту функцию, прежде чем отправил ее туда и обнаружил, что эта функция работает для меня.

Я очень надеюсь, что я полностью ответил на ваш вопрос, а также очень помог вам!

Ответ 10

Есть ли способ проверить, является ли итератор неразрешимым

Да, с gcc отладочными контейнерами, доступными как расширения GNU. Вместо std::list вы можете использовать __gnu_debug::list. Следующий код будет прерван, как только будет использован недопустимый итератор. Поскольку отладочные контейнеры налагают дополнительные накладные расходы, они предназначены только для отладки.

#include <debug/list>

int main() {
  __gnu_debug::list<int> l;
  for (int i = 1; i < 10; i++) {
    l.push_back(i * 10);
  }

  auto itd = l.begin();
  itd++;
  l.erase(itd);

  /* now, in other place.. check if itd points to somewhere meaningful */
  if (itd != l.end()) {
    //  blablabla
  }
}

$ ./a.out 
/usr/include/c++/7/debug/safe_iterator.h:552:
Error: attempt to compare a singular iterator to a past-the-end iterator.

Objects involved in the operation:
    iterator "lhs" @ 0x0x7ffda4c57fc0 {
      type = __gnu_debug::_Safe_iterator<std::_List_iterator<int>, std::__debug::list<int, std::allocator<int> > > (mutable iterator);
      state = singular;
      references sequence with type 'std::__debug::list<int, std::allocator<int> >' @ 0x0x7ffda4c57ff0
    }
    iterator "rhs" @ 0x0x7ffda4c580c0 {
      type = __gnu_debug::_Safe_iterator<std::_List_iterator<int>, std::__debug::list<int, std::allocator<int> > > (mutable iterator);
      state = past-the-end;
      references sequence with type 'std::__debug::list<int, std::allocator<int> >' @ 0x0x7ffda4c57ff0
    }
Aborted (core dumped)

Ответ 11

используйте стирание с шагом:

   if (something) l.erase(itd++);

чтобы вы могли проверить правильность итератора.