Изменение размеров вектора делают недействительным итераторы?

Вы могли бы хотеть объяснить, что Вы действительно хотите сделать.

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

, Например:

for (x = 0; x < 10; ++x) {
  for (y = 0; y < 5; ++y) {
    for (z = 0; z < 20; ++z) {
      DoSomething();
    }
  }
}

эквивалентно:

for (x = 0; x < 10*5*20; ++x) {
  DoSomething();
}
20
задан Johan Råde 13 September 2013 в 19:04
поделиться

5 ответов

Отредактировано с более тщательной формулировкой

да, изменение размера вектора может сделать недействительными все итераторы, указывающие на вектор.

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

Итак, ваши старые итераторы, которые указывают на старую память, больше не действительны. Однако если вектор изменить размер вниз (например, с помощью pop_back () ), то используется тот же массив. Размер массива никогда не уменьшается автоматически.

Один из способов избежать этого перераспределения (и недействительности указателя) - сначала вызвать vector :: reserve () , чтобы выделить достаточно места, чтобы в этом копировании не было необходимости . В вашем случае, если вы вызвали a.reserve (3) перед первой операцией push_back () , тогда внутренний массив будет достаточно большим, чтобы push_back могут быть выполнены без перераспределения массива, поэтому ваши итераторы останутся действительными.

28
ответ дан 29 November 2019 в 23:57
поделиться

Итераторы вектора становятся недействительными только тогда, когда вектор выполняет перераспределение.

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

7
ответ дан 29 November 2019 в 23:57
поделиться

Да, любое действие, которое может изменить размер вектора, может сделать итераторы недействительными.

Изменить: это включает операции (например, erase () , изменение размера () ), уменьшающие размер контейнера. erase () не делает недействительными все итераторы, но делает недействительными все итераторы, ссылающиеся на любую точку после стертого элемента (ов). resize () определяется в терминах insert () и erase () , поэтому имеет тот же потенциал.

3
ответ дан 29 November 2019 в 23:57
поделиться

Для справки в будущем, все виды лакомых кусочков STL, подобные этому, находятся на веб-сайте SGI: http://www.sgi.com/tech/stl/Vector.html

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

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

См. статью в Википедии о контейнерах Standard_Template_Library для начала. Если у вас есть деньги, я настоятельно рекомендую «Эффективный STL: 50 конкретных способов улучшить использование стандартной библиотеки шаблонов» Скотта Мейера.

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

0
ответ дан 29 November 2019 в 23:57
поделиться

Правила аннулирования итератора специфичны для контейнера.

Теперь аннулирование может иметь два значения с вектором:

  1. Недействительность = точка вне диапазона, определенного [begin, end ]
  2. Invalidation = указывает на объект, отличный от исходного

Как видите, второй гораздо более строгий:

std::vector<int> myVector;
myVector.push_back(0);
myVector.push_back(1);

std::vector<int>::iterator it = myVector.begin(); // it points to 0
myVector.erase(it); // it points to 1
myVector.erase(it); // it == myVector.end()

В этом случае он «действителен», поскольку он всегда находится во включительном range [начало, конец], поэтому его можно безопасно использовать для любой операции с myVector. С другой стороны, выражение (* it) постоянно меняется: сначала оно возвращает 0, затем 1, затем имеет неопределенное поведение ...

В общем, люди скорее будут говорить о втором требовании, а признание итератора недействительным просто означает что (* it) может не дать того же результата, что и раньше.


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

Во время добавления элементов:

  • Это может вызвать перераспределение (1) if myVector.size () == myVector.capacity (), поскольку проверка чревата ошибками, мы обычно считаем, что любое добавление аннулирует итераторы
  • . Если вы хотите быть «разборчивым» и знаете, что перераспределение не выполняется срабатывает, то вам все равно придется беспокоиться о insert . Вставка элемента делает недействительными итераторы, указывающие на эту текущую позицию и все последующие, поскольку элементы сдвигаются на один шаг в сторону конца вектора.

Во время удаления элементов:

  • Перераспределение не происходит, даже если буфер теперь намного больше, чем нужно. Тем не менее, это можно заставить используя сжатие для соответствия идиоме (2).
  • Все итераторы, указывающие за удаленным элементом, становятся недействительными. В частности, предыдущий итератор 'end' теперь находится за пределами диапазона [begin, end] и не может безопасно использоваться, например, в алгоритмах STL.

(1) Внутренняя структура std :: vector - это массив T, это связано с совместимостью с C-программами (с использованием & myVector.front () в качестве адреса массива) и тем, что он гарантирует непрерывность и минимальные накладные расходы на пространство (т. е. количество пространства, занимаемого собственными данными вектора, по сравнению с количеством пространства, занимаемого объектом)

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

Когда вы хотите вставить объект, а вектор не имеет необходимой емкости , запускается вызов метода .reserve (size_t). Этот метод, который будет выделять столько памяти, сколько необходимо (минимум в зависимости от библиотеки), и копировать элементы myVector. Затем операция подкачки обменивает буферы из myVector и этой копии, и, таким образом, myVector теперь хранит буфер с минимально необходимым объемом памяти. В конце операции временное устройство уничтожается, а хранимая в нем память освобождается.

1
ответ дан 29 November 2019 в 23:57
поделиться
Другие вопросы по тегам:

Похожие вопросы: