iterate vetor, remover certos itens como eu ir
-
05-07-2019 - |
Pergunta
Eu tenho um std :: vector m_vPaths; I irá iterar este vetor e chamada :: DeleteFile (strPath) como eu ir. Se eu excluir o arquivo com êxito, vou removê-lo do vetor. A minha pergunta é que eu posso dar a volta ter que usar dois vetores? Existe estrutura de dados diferente que poderia ser mais adequado para o que eu preciso fazer?
exemplo: usando iteradores quase faz o que eu quero, mas o problema é quando você apagar usando um iterador, todos os iteradores se tornar inválido.
std::vector<std::string> iter = m_vPaths.begin();
for( ; iter != m_vPaths.end(); iter++) {
std::string strPath = *iter;
if(::DeleteFile(strPath.c_str())) {
m_vPaths.erase(iter);
//Now my interators are invalid because I used erase,
//but I want to continue deleteing the files remaining in my vector.
}
}
Eu posso usar dois vetores e eu já não vai ter um problema, mas existe um método melhor, mais eficiente de fazer o que estou tentando fazer?
btw, meter não está claro, m_vPaths é declarado como este (em minha classe):
std::vector<std::string> m_vPaths;
Solução
Confira std::remove_if
:
#include <algorithm> // for remove_if
#include <functional> // for unary_function
struct delete_file : public std::unary_function<const std::string&, bool>
{
bool operator()(const std::string& strPath) const
{
return ::DeleteFile(strPath.c_str());
}
}
m_vPaths.erase(std::remove_if(m_vPaths.begin(), m_vPaths.end(), delete_file()),
m_vPaths.end());
Use a std::list
para parar o problema iterators inválido, embora você perde aleatório Acesso. (E o desempenho do cache, em geral)
Para o registro, o modo como você poderia implementar o código seria:
typedef std::vector<std::string> string_vector;
typedef std::vector<std::string>::iterator string_vector_iterator;
string_vector_iterator iter = m_vPaths.begin();
while (iter != m_vPaths.end())
{
if(::DeleteFile(iter->c_str()))
{
// erase returns the new iterator
iter = m_vPaths.erase(iter);
}
else
{
++iter;
}
}
Mas você deve usar std::remove_if
(reinventar a roda é ruim).
Outras dicas
erase()
método retorna uma nova (válidos) iterador que pontos para o próximo elemento após o apagado um. Você pode usar este iterador para continuar com o loop:
std::vector<std::string>::iterator iter;
for (iter = m_vPaths.begin(); iter != m_vPaths.end(); ) {
if (::DeleteFile(iter->c_str()))
iter = m_vPaths.erase(iter);
else
++iter;
}
Dado o tempo para apagar um arquivo, ele provavelmente não importa, mas eu ainda gostaria orientar a iteração através das costas do vetor - de que maneira você está excluindo normalmente itens de (cerca de) o fim do vetor. O tempo necessário para excluir um item é proporcional ao número de itens que o seguem no vetor. Se (por exemplo) você tem um vetor de 100 nomes de arquivo e eliminar com sucesso todos eles, você vai copiar o último elemento 100 vezes no processo (e copiar o segundo ao último elemento de 99 vezes, e assim por diante).
OTOH, se você começar a partir do final e trabalhar para trás, você não copiar enquanto apagando os arquivos é bem sucedida. Você pode usar iteradores reversos para atravessar os para trás vetor sem mudar muito do que qualquer outra coisa. Por exemplo, o código de GMan usando remove_if deve continuar a trabalhar (apenas um pouco mais rápido) simplesmente substituindo rbegin () para começar () e rend () para o final.
Outra possibilidade é usar um deque em vez de um vetor -. Um deque pode apagar itens a partir do final ou o início da coleção em tempo constante