Изменение структуры данных при итерации по ней
Что происходит, когда вы добавляете элементы в структуру данных, такую как вектор, в то время как
итерации по нему. Могу ли я это сделать?
Я пробовал это, и он ломается:
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
указать максимальное количество записей, которые вы могли бы добавить. это остановит вектор от необходимости изменения размера, и это должно предотвратить сбои