У меня есть вектор IInventory *, и я просматриваю список, используя диапазон C ++ 11 для работы с каждым из них.
Проделав кое-что с одним, я могу удалить его из списка и удалить объект. Я знаю, что могу вызвать delete
указатель в любое время, чтобы очистить его, но как правильно удалить его из вектора, находясь в for
цикле диапазона ? И если я удалю его из списка, мой цикл станет недействительным?
std::vector<IInventory*> inv;
inv.push_back(new Foo());
inv.push_back(new Bar());
for (IInventory* index : inv)
{
// Do some stuff
// OK, I decided I need to remove this object from 'inv'...
}
std::remove_if
с предикатом, который «делает что-то», а затем возвращает true, если вы хотите удалить элемент.std::list
нижеОтветы:
Нет, не можешь. На основе диапазона
for
используется, когда вам нужно получить доступ к каждому элементу контейнера один раз.Вы должны использовать обычный
for
цикл или один из его родственников, если вам нужно изменить контейнер по мере продвижения, получить доступ к элементу более одного раза или иным образом выполнить итерацию нелинейным образом через контейнер.Например:
источник
true
, AFAIU, и кажется, что таким способом лучше не смешивать логику итераций с предикатом.remove_if
лучше.erase
возвращает новый действительный итератор. это может быть неэффективно, но гарантированно работает.Каждый раз, когда элемент удаляется из вектора, вы должны предполагать, что итераторы на удаленном элементе или после него больше не действительны, потому что каждый из элементов, следующих за удаленным элементом, перемещается.
Цикл for на основе диапазона - это просто синтаксический сахар для «нормального» цикла, использующего итераторы, поэтому применимо вышеизложенное.
При этом вы можете просто:
источник
vector
никогда не перераспределение в связи с вызовомerase
. Причина, по которой итераторы становятся недействительными, состоит в том, что перемещаются все элементы, следующие за удаленным элементом.[&]
бы целесообразно, чтобы позволить ему «делать некоторые вещи» с локальными переменными.remove_if
с.erase
, в противном случае ничего не происходит.std::remove_if
равно O (n).В идеале вы не должны изменять вектор во время итерации по нему. Используйте идиому «стереть-удалить». Если вы это сделаете, вы, вероятно, столкнетесь с несколькими проблемами. Поскольку в недействительными все итераторы , начиная с элемента стирается до величин вам нужно будет убедиться , что ваши итераторы остаются действительными с помощью:
vector
erase
end()
Обратите внимание, что вам нужен
b != v.end()
тест «как есть». Если вы попытаетесь оптимизировать его следующим образом:вы столкнетесь с UB, так как ваш
e
становится недействительным после первогоerase
вызова.источник
std::remove
, и это O (N ^ 2), а не O (N).Строгое ли требование удалять элементы в этом цикле? В противном случае вы можете установить указатели, которые хотите удалить, на NULL и сделать еще один проход по вектору, чтобы удалить все указатели NULL.
источник
извините за некропостинг и также извините, если мой опыт в C ++ мешает моему ответу, но если вы пытаетесь перебрать каждый элемент и внести возможные изменения (например, стереть индекс), попробуйте использовать обратные слова для цикла.
при стирании индекса x следующий цикл будет для элемента «перед» последней итерацией. я очень надеюсь, что это помогло кому-то
источник
Хорошо, я опоздала, но все равно: К сожалению, не правильно , что я читал до сих пор - это это возможно, вам просто нужно два итератора:
Простое изменение значения, на которое указывает итератор, не делает недействительным любой другой итератор, поэтому мы можем сделать это, не беспокоясь. На самом деле
std::remove_if
(по крайней мере, реализация gcc) делает что-то очень похожее (используя классический цикл ...), просто ничего не удаляет и не стирает.Однако имейте в виду, что это не потокобезопасный (!) - однако это также относится и к некоторым другим решениям, указанным выше ...
источник
erase
(при условии, конечно, что вы удалите более одного элемента)?Я покажу на примере ниже пример удаления нечетных элементов из вектора:
вывод aw ниже:
Имейте в виду, что метод
erase
вернет следующий итератор переданного итератора.Из здесь , мы можем использовать более генерировать метод:
См. Здесь, чтобы узнать, как использовать
std::remove_if
. https://en.cppreference.com/w/cpp/algorithm/removeисточник
В противовес этому заголовку темы я бы использовал два прохода:
источник
Гораздо более элегантным решением было бы переключиться на
std::list
(при условии, что вам не нужен быстрый произвольный доступ).Затем вы можете удалить с помощью
.remove_if
и функтор C ++ в одной строке:Итак, здесь я просто пишу функтор, который принимает один аргумент (
Widget*
). Возвращаемое значение - это условие, при котором необходимо удалитьWidget*
из списка.Я считаю этот синтаксис приемлемым. Я не думаю , что я когда - либо использовать
remove_if
для STD :: векторов - есть так многоinv.begin()
иinv.end()
шум там вы , вероятно , лучше использовать целый индекс на основе удаление или просто обычные старые регулярный итератор на основе удаления (как показано ниже). Но вstd::vector
любом случае вам не следует удалять из середины очень много, поэтому рекомендуется переключаться на alist
в этом случае частого удаления середины списка.Обратите внимание, однако, у меня не было возможности позвонить
delete
вWidget*
удаленные. Для этого это будет выглядеть так:Вы также можете использовать обычный цикл на основе итератора, например:
Если вам не нравится длина
for( list<Widget*>::iterator iter = widgets.begin() ; ...
, вы можете использоватьисточник
remove_if
наstd::vector
самом деле работает, и как он сохраняет сложность до O (N).std::vector
всегда будет сдвигать каждый элемент после того, который вы удалили, на один вверх, что делаетstd::list
гораздо лучший выбор.remove_if
сдвинет каждый элемент вверх на количество освобожденных пробелов. К тому времени , ваша учетная запись для использования кэша,remove_if
наstd::vector
вероятного удаления обгоняет от аstd::list
. И сохраняетO(1)
произвольный доступ.Думаю, я бы сделал следующее ...
источник
вы не можете удалить итератор во время итерации цикла, потому что количество итераторов не совпадает, и после некоторой итерации у вас будет недопустимый итератор.
Решение: 1) возьмите копию исходного вектора 2) повторите итератор, используя эту копию 2) сделайте что-нибудь и удалите его из исходного вектора.
источник
Стирание элементов по одному легко приводит к производительности N ^ 2. Лучше отметить элементы, которые нужно стереть, и стереть их сразу после цикла. Если я могу предположить nullptr в недопустимом элементе в вашем векторе, тогда
должно сработать.
В случае, если ваш «Сделай что-нибудь» не изменяет элементы вектора и используется только для принятия решения об удалении или сохранении элемента, вы можете преобразовать его в лямбда (как было предложено в чьей-то более ранней публикации) и использовать
источник