Изменение структуры данных при итерации по ней

Что происходит, когда вы добавляете элементы в структуру данных, такую ​​как вектор, в то время как итерации по нему. Могу ли я это сделать?

Я пробовал это, и он ломается:

int main() {
    vector<int> x = { 1, 2, 3 };

    int j = 0;
    for (auto it = x.begin(); it != x.end(); ++it) {
        x.push_back(j);
        j++;
        cout << j << " .. ";
    }
}

Ответы

Ответ 1

Итераторы недействительны некоторыми операциями, которые изменяют std::vector.

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

Кстати, функция entrypoint main() MUST возвращает int:

int main() { ... }

Ответ 2

Что происходит, когда вы добавляете элементы в структуру данных, такие как вектор, итерации по ней. Могу ли я этого не делать?

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

Я предлагаю вам избежать этого.

Краткое объяснение, почему изменение размера делает недействительным итератор:

Изначально вектор имеет некоторую пропускную способность (которую вы можете узнать, вызывая vector::capacity().), и вы добавляете в него элементы, а когда он становится заполненным, он выделяет больший объем памяти, копируя элементы из старой памяти в вновь выделенной памяти, а затем удаляет старую память, и проблема в том, что итератор все еще указывает на старую память, которая была освобождена. Таким образом, изменение размера делает недействительным итератор.

Вот простая демонстрация. Просто посмотрите, когда изменяется capacity:

std::vector<int> v;
for(int i = 0 ; i < 100 ; i++ )
{
  std::cout <<"size = "<<v.size()<<", capacity = "<<v.capacity()<<std::endl;
  v.push_back(i);
}       

Частичный выход:

size = 0, capacity = 0
size = 1, capacity = 1
size = 2, capacity = 2
size = 3, capacity = 4
size = 4, capacity = 4
size = 5, capacity = 8
size = 6, capacity = 8
size = 7, capacity = 8
size = 8, capacity = 8
size = 9, capacity = 16
size = 10, capacity = 16

Смотрите полный вывод здесь: http://ideone.com/rQfWe

Примечание: capacity() указывает максимальное количество элементов, которые вектор может содержать, не выделяя новую память, а size() указывает количество элементов, которые в настоящее время содержат вектор.

Ответ 3

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

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

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

vector<int> temp;

// ...

// Inside loop, do this:
temp.push_back(j);

// ...

// After loop, do this to insert all new elements onto end of x
x.insert(x.end(), temp.begin(), temp.end());

Ответ 4

Это не очень хорошая идея.

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

Ответ 5

В то время как вы использовали вектор в качестве примера, существуют другие stl-контейнеры, которые могут иметь элементы, отброшенные назад, без аннулирования итераторов. Отбрасывание элемента в std:: list не требует перераспределения существующих элементов, поскольку они не хранятся смежно (списки вместо этого состоят из узлов, связанных друг с другом указателями на следующий node), поэтому итераторы остаются действительными поскольку node они внутренне указывают на то, что все еще находятся по одному адресу.

Ответ 6

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