Проверка допустимости итератора - PullRequest
61 голосов
/ 14 января 2010

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

Я использовал 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
}

Ответы [ 11 ]

57 голосов
/ 14 января 2010

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

25 голосов
/ 14 января 2010

Как сказал 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
10 голосов
/ 14 января 2010

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

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

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

Редактировать Также я должен добавить: отладочные итераторы в visual studio предназначены для немедленного взрыва при их использовании (вместо неопределенного поведения); не допускать «опроса» своего государства.

9 голосов
/ 14 января 2010

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

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

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

6 голосов
/ 14 января 2010

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

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

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

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

Обходные пути, такие как перехват исключений, не сработают.

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

3 голосов
/ 14 января 2010

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

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

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

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

1 голос
/ 12 ноября 2017

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

Да, с 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)
1 голос
/ 07 марта 2010

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

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

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

0 голосов
/ 14 января 2015

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

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

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

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

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

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

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

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

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

Я сделал функцию, которая проверяет, тестирует, знает и возвращает истину, является ли данный итератор недействительным или нет. Вы можете использовать функцию 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".
}

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

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

0 голосов
/ 20 января 2010

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

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

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

...