Проверить, если итератор действует



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



Я использую 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
}
735   11  

11 ответов:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

есть ли способ проверить, если итератор является уникальным

да, с 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)
if (iterator != container.end()) {
   iterator is dereferencable !
}

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

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

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

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

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

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

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

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

это тоже несопоставимо!!! Т. е. вы даже не можете использовать ни == ни != операторы

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

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

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

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

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

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

Comments

    Ничего не найдено.