Tenho um vetor de IInventory * e estou percorrendo a lista usando o intervalo C ++ 11 para, para fazer coisas com cada um.
Depois de fazer algumas coisas com um, posso querer removê-lo da lista e excluir o objeto. Sei que posso chamar delete
o ponteiro a qualquer momento para limpá-lo, mas qual é a maneira correta de removê-lo do vetor, enquanto está no for
loop de intervalo ? E se eu removê-lo da lista, meu loop será invalidado?
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
com um predicado que "faz coisas" e retornar true se quiser que o elemento seja removido.std::list
abaixoRespostas:
Não, você não pode. Baseado em intervalo
for
é para quando você precisa acessar cada elemento de um contêiner uma vez.Você deve usar o
for
loop normal ou um de seus primos se precisar modificar o contêiner à medida que avança, acessar um elemento mais de uma vez ou, de outra forma, iterar de forma não linear por meio do contêiner.Por exemplo:
fonte
true
, AFAIU, e parece melhor dessa forma não misturar a lógica de iteração com o predicado.remove_if
é melhor.erase
retorna um novo iterador válido. pode não ser eficiente, mas funciona com certeza.Cada vez que um elemento é removido do vetor, você deve assumir que os iteradores no momento ou após o elemento apagado não são mais válidos, porque cada um dos elementos que sucedem o elemento apagado são movidos.
Um for-loop baseado em intervalo é apenas açúcar sintático para loop "normal" usando iteradores, portanto, o acima se aplica.
Dito isso, você pode simplesmente:
fonte
vector
nunca será realocado devido a uma chamada aerase
. A razão pela qual os iteradores são invalidados é porque cada um dos elementos que sucedem ao elemento apagado é movido.[&]
seria apropriado, para permitir que ele "faça algumas coisas" com variáveis locais.remove_if
com.erase
, caso contrário, nada acontece.std::remove_if
é O (n).remove_if
deve ser feito internamente). No entanto, se você tem umvector
dos 5 elementos e você só.erase()
1 de cada vez, não há impacto no desempenho para o uso de iteradores vsremove_if
. Se a lista for maior, você realmente deveria mudar para um localstd::list
onde há muitas remoções no meio da lista.Idealmente, você não deve modificar o vetor durante a iteração sobre ele. Use o idioma apagar-remover. Se você fizer isso, provavelmente encontrará alguns problemas. Como em um
vector
anerase
invalida todos os iteradores começando com o elemento sendo apagado até o,end()
você precisará se certificar de que seus iteradores permanecem válidos usando:Observe que você precisa do
b != v.end()
teste no estado em que se encontra. Se você tentar otimizá-lo da seguinte maneira:você irá se deparar com UB, pois seu
e
é invalidado após a primeiraerase
chamada.fonte
std::remove
e é O (N ^ 2), não O (N).É um requisito estrito remover elementos durante esse loop? Caso contrário, você pode definir os ponteiros que deseja excluir como NULL e fazer outra passagem sobre o vetor para remover todos os ponteiros NULL.
fonte
desculpe por necroposting e também desculpe se minha experiência em c ++ atrapalhar minha resposta, mas se você tentar iterar por cada item e fazer alterações possíveis (como apagar um índice), tente usar um backwords para loop.
ao apagar o índice x, o próximo loop será para o item "na frente" da última iteração. eu realmente espero que isso ajude alguém
fonte
OK, estou atrasado, mas de qualquer maneira: Desculpe, não corrija o que li até agora - é possível, você só precisa de dois iteradores:
Apenas modificar o valor para o qual um iterador aponta não invalida nenhum outro iterador, portanto, podemos fazer isso sem ter que nos preocupar. Na verdade,
std::remove_if
(implementação gcc pelo menos) faz algo muito semelhante (usando um loop clássico ...), apenas não exclui nada e não apaga.Esteja ciente, no entanto, que isso não é seguro para thread (!) - no entanto, isso também se aplica a algumas das outras soluções acima ...
fonte
erase
(desde que exclua mais de um único elemento, é claro)?Vou mostrar com um exemplo, o exemplo abaixo remove elementos estranhos do vetor:
saída aw abaixo:
Lembre-se de que o método
erase
retornará o próximo iterador do iterador passado.A partir daqui , podemos usar um método mais gerador:
Veja aqui para ver como usar
std::remove_if
. https://en.cppreference.com/w/cpp/algorithm/removefonte
Em oposição a este título de tópicos, eu usaria dois passes:
fonte
Uma solução muito mais elegante seria mudar para
std::list
(assumindo que você não precisa de acesso aleatório rápido).Você pode então excluir com
.remove_if
um functor C ++ em uma linha:Então, aqui estou apenas escrevendo um functor que aceita um argumento (o
Widget*
). O valor de retorno é a condição na qual remover umWidget*
da lista.Acho essa sintaxe palatável. Eu não acho que eu nunca usaria
remove_if
para std :: vetores - há muitoinv.begin()
einv.end()
barulho lá você é provavelmente melhor fora de usar uma base-integer-índice de exclusão ou apenas uma planície antiga regular de exclusão (como mostrado à base de iterador abaixo). Mas você não deve realmente remover do meio de umstd::vector
qualquer maneira, entãolist
é aconselhável mudar para um neste caso de exclusão frequente no meio da lista.Observe, entretanto, que não tive a chance de ligar
delete
para osWidget*
que foram removidos. Para fazer isso, ficaria assim:Você também pode usar um loop baseado em iterador regular como:
Se você não gosta do comprimento de
for( list<Widget*>::iterator iter = widgets.begin() ; ...
, você pode usarfonte
remove_if
umstd::vector
funciona realmente e como mantém a complexidade em O (N).std::vector
sempre fará deslizar cada elemento após o que você removeu, tornando umastd::list
escolha muito melhor.remove_if
irá deslizar cada elemento para cima pelo número de espaços livres. No momento em que você contabiliza o uso do cache,remove_if
em umstd::vector
provavelmente supera a remoção de astd::list
. E preserva oO(1)
acesso aleatório.Acho que faria o seguinte ...
fonte
você não pode excluir o iterador durante a iteração do loop porque a contagem do iterador não corresponde e, após alguma iteração, você teria um iterador inválido.
Solução: 1) pegar a cópia do vetor original 2) iterar o iterador usando esta cópia 2) fazer algumas coisas e excluí-lo do vetor original.
fonte
Apagar um elemento por um leva facilmente ao desempenho do N ^ 2. Melhor marcar os elementos que devem ser apagados e apagá-los imediatamente após o loop. Se eu posso presumir nullptr em um elemento inválido em seu vetor, então
Deveria trabalhar.
Caso o seu "Faça alguma coisa" não esteja mudando elementos do vetor e apenas seja usado para tomar a decisão de remover ou manter o elemento, você pode convertê-lo para lambda (como foi sugerido em um post anterior de alguém) e usar
fonte