Pegue as duas linhas de código a seguir:
for (int i = 0; i < some_vector.size(); i++)
{
//do stuff
}
E isto:
for (some_iterator = some_vector.begin(); some_iterator != some_vector.end();
some_iterator++)
{
//do stuff
}
Disseram-me que a segunda maneira é preferida. Por que exatamente é isso?
some_iterator++
para++some_iterator
. O pós-incremento cria um iterador temporário desnecessário.end()
a cláusula de declaração.vector::end
provavelmente tem problemas piores com que se preocupar do que se é extraído de loops ou não. Pessoalmente, prefiro a clareza - se fosse uma ligaçãofind
na condição de término, eu me preocuparia.it != vec.end()
eit != end
, é entre(vector<T>::iterator it = vec.begin(); it != vec.end(); ++it)
e(vector<T>::iterator it = vec.begin(), end = vec.end(); it != end; ++it)
. Não preciso contar os personagens. Por todos os meios, prefira um sobre o outro, mas o desacordo de outras pessoas com a sua preferência não é "negligência", é uma preferência por código mais simples com menos variáveis e, portanto, menos em que pensar ao lê-lo.Respostas:
A primeira forma é eficiente apenas se vector.size () for uma operação rápida. Isso vale para vetores, mas não para listas, por exemplo. Além disso, o que você planeja fazer dentro do corpo do loop? Se você planeja acessar os elementos como em
então você está assumindo que o contêiner
operator[](std::size_t)
definiu. Novamente, isso é verdade para vetores, mas não para outros contêineres.O uso de iteradores aproxima você da independência do contêiner . Você não está fazendo suposições sobre a capacidade de acesso aleatório ou
size()
operação rápida , apenas que o contêiner possui recursos de iterador.Você pode aprimorar ainda mais seu código usando algoritmos padrão. Dependendo do que é que você está tentando alcançar, você pode optar por usar
std::for_each()
,std::transform()
e assim por diante. Ao usar um algoritmo padrão em vez de um loop explícito, você evita reinventar a roda. É provável que seu código seja mais eficiente (dado que o algoritmo correto foi escolhido), correto e reutilizável.fonte
size_t
variável de membro acompanhandosize()
.size()
função de membro precisará ter uma complexidade de tempo constante para todos os contêineres que a suportam, inclusivestd::list
.Faz parte do processo moderno de doutrinação em C ++. Os iteradores são a única maneira de iterar a maioria dos contêineres; portanto, você o usa mesmo com vetores apenas para entrar na mentalidade adequada. Sério, é a única razão pela qual faço isso - acho que nunca substituí um vetor por um tipo diferente de contêiner.
Uau, isso ainda está sendo votado depois de três semanas. Eu acho que não vale a pena ser um pouco irônico.
Eu acho que o índice da matriz é mais legível. Ele corresponde à sintaxe usada em outros idiomas e à sintaxe usada para matrizes C à moda antiga. Também é menos detalhado. A eficiência deve ser uma lavagem, se o seu compilador for bom, e quase não há casos em que isso seja importante.
Mesmo assim, continuo usando iteradores frequentemente com vetores. Eu acredito que o iterador é um conceito importante, então eu o promovo sempre que posso.
fonte
porque você não está vinculando seu código à implementação específica da lista some_vector. se você usar índices de matriz, deve ser alguma forma de matriz; se você usar iteradores, poderá usar esse código em qualquer implementação de lista.
fonte
Imagine que algum_vector seja implementado com uma lista vinculada. A solicitação de um item no i-ésimo lugar exige que sejam executadas i operações para percorrer a lista de nós. Agora, se você usar o iterador, em geral, fará o possível para ser o mais eficiente possível (no caso de uma lista vinculada, ele manterá um ponteiro para o nó atual e o avançará em cada iteração, exigindo apenas uma operação única).
Portanto, fornece duas coisas:
fonte
Eu serei o advogado do diabo aqui, e não recomendo iteradores. A principal razão pela qual, é todo o código-fonte em que trabalhei, desde o desenvolvimento de aplicativos da área de trabalho até o desenvolvimento de jogos, nem tenho que usar iteradores. O tempo todo eles não são necessários e, em segundo lugar, as suposições ocultas, os códigos e os pesadelos de depuração que você obtém com os iteradores fazem deles um excelente exemplo para não usá-lo em aplicativos que exijam velocidade.
Mesmo do ponto de vista da manutenção, eles são uma bagunça. Não é por causa deles, mas por causa de todo o aliasing que acontece nos bastidores. Como sei que você não implementou seu próprio vetor virtual ou lista de matrizes que faz algo completamente diferente dos padrões. Eu sei que tipo é atualmente atualmente durante o tempo de execução? Você sobrecarregou um operador? Não tive tempo de verificar todo o seu código-fonte. Inferno, eu sei mesmo qual versão do STL você está usando?
O próximo problema que você teve com os iteradores é a abstração com vazamento, embora existam vários sites que discutem isso em detalhes com eles.
Desculpe, ainda não vi nenhum ponto nos iteradores. Se eles abstraem a lista ou o vetor de você, quando na verdade você já deve saber com qual vetor ou lista está lidando, se não o fizer, estará se preparando para algumas ótimas sessões de depuração no futuro.
fonte
Você pode usar um iterador se quiser adicionar / remover itens ao vetor enquanto estiver iterando sobre ele.
Se você estivesse usando índices, teria que embaralhar os itens para cima / baixo na matriz para manipular as inserções e exclusões.
fonte
std::list
comparação astd::vector
, no entanto, se você estiver recomendando o uso de uma lista vinculada em vez de astd::vector
. Consulte a página 43: ecn.channel9.msdn.com/events/GoingNative12/GN12Cpp11Style.pdf Na minha experiência, descobri que umstd::vector
é mais rápido que um,std::list
mesmo que esteja pesquisando sobre tudo e removendo elementos em posições arbitrárias.for (node = list->head; node != NULL; node = node->next)
mais curta do que suas duas primeiras linhas de código juntas (declaração e cabeçalho de loop). Então eu digo novamente - não há muita diferença fundamental na brevidade entre usar iteradores e não usá-los - você ainda satisfaz as três partes de umafor
declaração, mesmo se usarwhile
: declare, repita, verifique a terminação.Separação de preocupações
É muito bom separar o código de iteração da preocupação principal do loop. É quase uma decisão de design.
De fato, a iteração por índice vincula você à implementação do contêiner. Solicitar ao contêiner um iterador inicial e final, habilita o código do loop para uso com outros tipos de contêineres.
Além disso,
std::for_each
você diz à coleção o que fazer, em vez de perguntar algo sobre seus componentes internos.O padrão 0x introduzirá fechamentos, o que tornará essa abordagem muito mais fácil de usar - veja o poder expressivo de, por exemplo, o Ruby
[1..6].each { |i| print i; }
...atuação
Mas talvez uma questão muito supervisionada seja a de que, usando a
for_each
abordagem, haja uma oportunidade de paralelizar a iteração - os blocos de encadeamento de intel podem distribuir o bloco de código pelo número de processadores no sistema!Nota: depois de descobrir a
algorithms
biblioteca e, principalmenteforeach
, passei dois ou três meses escrevendo estruturas ridiculamente pequenas de operadores 'auxiliares' que enlouquecerão seus colegas desenvolvedores. Após esse período, voltei a uma abordagem pragmática - pequenos corpos em loop não merecemforeach
mais :)Uma referência de leitura obrigatória nos iteradores é o livro "Extended STL" .
O GoF possui um pequeno parágrafo no final do padrão Iterator, que fala sobre esse tipo de iteração; é chamado de 'iterador interno'. Dê uma olhada aqui também.
fonte
Porque é mais orientado a objetos. se você estiver iterando com um índice, estará assumindo:
a) que esses objetos são ordenados
b) que esses objetos podem ser obtidos por um índice
c) que o incremento do índice atingirá todos os itens
d) que esse índice comece em zero
Com um iterador, você está dizendo "me dê tudo para que eu possa trabalhar com ele" sem saber qual é a implementação subjacente. (Em Java, há coleções que não podem ser acessadas por meio de um índice)
Além disso, com um iterador, não é necessário se preocupar em sair dos limites da matriz.
fonte
Outra coisa interessante dos iteradores é que eles permitem que você expresse (e imponha) sua preferência constante. Este exemplo garante que você não alterará o vetor no meio do seu loop:
fonte
const_iterator
. Se eu altero o vetor no loop, faço-o por uma razão e, por 99,9% do tempo, alterar não é um acidente e, pelo resto, é apenas um bug como qualquer tipo de erro no código do autor precisa consertar. Como em Java e em muitas outras linguagens, não há nenhum objeto const, mas os usuários dessas linguagens nunca têm um problema sem suporte const nessas linguagens.const_iterator
, então qual poderia ser o motivo?Além de todas as outras excelentes respostas ...
int
pode não ser grande o suficiente para o seu vetor. Em vez disso, se você deseja usar a indexação, use osize_type
para seu contêiner:fonte
int i
amyvector.size()
.Eu provavelmente devo salientar que você também pode ligar
std::for_each(some_vector.begin(), some_vector.end(), &do_stuff);
fonte
Os iteradores STL estão principalmente lá, de modo que os algoritmos STL, como sort, podem ser independentes de contêineres.
Se você quiser fazer um loop sobre todas as entradas em um vetor, use o estilo de loop de índice.
É menos digitado e mais fácil de analisar para a maioria dos humanos. Seria bom se o C ++ tivesse um loop foreach simples sem exagerar na mágica de modelos.
fonte
Eu não acho que faz muita diferença para um vetor. Eu prefiro usar um índice, pois considero mais legível e você pode acessar aleatoriamente, como pular para a frente 6 itens ou pular para trás, se necessário.
Também gosto de fazer uma referência ao item dentro do loop dessa maneira, para que não haja muitos colchetes ao redor do local:
Usar um iterador pode ser bom se você achar que poderá precisar substituir o vetor por uma lista em algum momento no futuro e também parecer mais elegante para os malucos do STL, mas não consigo pensar em nenhum outro motivo.
fonte
I prefer to use an index myself as I consider it to be more readable
somente em algumas situações; em outros, os índices rapidamente ficam muito confusos.and you can do random access
que não é um recurso exclusivo dos índices: consulte en.cppreference.com/w/cpp/concept/RandomAccessIteratorA segunda forma representa o que você está fazendo com mais precisão. No seu exemplo, você realmente não se importa com o valor de i - tudo o que você quer é o próximo elemento no iterador.
fonte
Depois de ter aprendido um pouco mais sobre o assunto desta resposta, percebo que foi um pouco simplista demais. A diferença entre este loop:
E esse loop:
É bastante mínimo. De fato, a sintaxe de fazer loops dessa maneira parece estar crescendo em mim:
Os iteradores desbloqueiam alguns recursos declarativos bastante poderosos e, quando combinados com a biblioteca de algoritmos STL, você pode fazer algumas coisas bem legais que estão fora do escopo da administração de índices de matriz.
fonte
for (Iter it = {0}; it != end; ++it) {...}
- você deixou de fora a declaração -, portanto, a brevidade não é muito diferente do seu segundo exemplo. Ainda assim, +1.A indexação requer uma
mul
operação extra . Por exemplo, paravector<int> v
, o compilador é convertidov[i]
em&v + sizeof(int) * i
.fonte
sizeof
e apenas adicioná-lo uma vez por iteração, em vez de fazer todo o cálculo do deslocamento novamente todas as vezes.Durante a iteração, você não precisa saber o número de itens a serem processados. Você só precisa do item e os iteradores fazem essas coisas muito bem.
fonte
Ninguém mencionou ainda que uma vantagem dos índices é que eles não se tornam inválidos quando você anexa a um contêiner contíguo como
std::vector
, portanto, você pode adicionar itens ao contêiner durante a iteração.Isso também é possível com os iteradores, mas você deve ligar
reserve()
e, portanto, precisa saber quantos itens anexará.fonte
Vários bons pontos já. Tenho alguns comentários adicionais:
Supondo que estamos falando sobre a biblioteca padrão C ++, "vetor" implica em um contêiner de acesso aleatório que possui as garantias da matriz C (acesso aleatório, layout de memória contíguo etc.). Se você tivesse dito 'some_container', muitas das respostas acima teriam sido mais precisas (independência do contêiner etc.).
Para eliminar quaisquer dependências na otimização do compilador, você pode mover some_vector.size () para fora do loop no código indexado, da seguinte maneira:
Sempre pré-incremente os iteradores e trate os pós-incrementos como casos excepcionais.
Portanto, assumindo e indexável
std::vector<>
como o contêiner, não há uma boa razão para preferir um ao outro, passando sequencialmente pelo contêiner. Se você precisar consultar com freqüência índices mais antigos ou mais recentes, a versão indexada é mais apropriada.Em geral, é preferível usar os iteradores porque os algoritmos os utilizam e o comportamento pode ser controlado (e implicitamente documentado) alterando o tipo do iterador. Os locais da matriz podem ser usados no lugar dos iteradores, mas a diferença sintática será destacada.
fonte
Não uso iteradores pelo mesmo motivo que não gosto de declarações foreach. Ao ter vários loops internos, é bastante difícil acompanhar as variáveis globais / membros sem ter que lembrar todos os valores locais e nomes de iteradores também. O que acho útil é usar dois conjuntos de índices para diferentes ocasiões:
Eu nem quero abreviar coisas como "animation_matrices [i]" para algum iterador aleatório "anim_matrix"-named-iterator, por exemplo, porque então você não pode ver claramente de qual matriz esse valor é originado.
fonte
it
,jt
,kt
, etc., ou mesmo continuar usandoi
,j
,k
, etc. E se você precisa saber exatamente o que um iterador representa, em seguida, para mim algo comofor (auto anim = anims.begin(); ...) for (auto anim_bone = anim->bones.begin(); ...) anim_bone->wobble()
seria mais descritivo do que ter que indexar continuamente comoanimation_matrices[animIndex][boneIndex]
.for
loop baseado em intervalo , o que torna a maneira baseada em iterador de fazer isso ainda mais concisa.Realmente, isso é tudo o que existe. Não é como se você fosse ganhar mais concisão de qualquer maneira, em média, e se a concisão realmente é seu objetivo, você sempre pode recorrer às macros.
fonte
Se você tiver acesso aos recursos do C ++ 11 , também poderá usar um loop baseado em intervalo
for
para iterar sobre seu vetor (ou qualquer outro contêiner) da seguinte maneira:O benefício desse loop é que você pode acessar elementos do vetor diretamente através da
item
variável, sem correr o risco de bagunçar um índice ou cometer um erro ao fazer a referência a um iterador. Além disso, o espaço reservadoauto
impede que você precise repetir o tipo dos elementos do contêiner, o que o aproxima ainda mais de uma solução independente do contêiner.Notas:
operator[]
existe para o seu contêiner (e é rápido o suficiente para você), é melhor seguir seu primeiro caminho.for
loop baseado em intervalo não pode ser usado para adicionar / excluir elementos em / de um contêiner. Se você quiser fazer isso, é melhor seguir a solução dada por Brian Matthews.const
da seguinte forma:for (auto const &item : some_vector) { ... }
.fonte
Ainda melhor do que "dizer à CPU o que fazer" (imperativo) é "dizer às bibliotecas o que você deseja" (funcional).
Então, ao invés de usar loops, você deve aprender os algoritmos presentes no stl.
fonte
Para independência do contêiner
fonte
Eu sempre uso o índice de matriz, porque muitas aplicações minhas exigem algo como "exibir imagem em miniatura". Então eu escrevi algo como isto:
fonte
Ambas as implementações estão corretas, mas eu preferiria o loop 'for'. Como decidimos usar um vetor e não qualquer outro contêiner, o uso de índices seria a melhor opção. O uso de iteradores com vetores perderia o benefício de ter os objetos em blocos de memória contínua, o que ajuda a facilitar o acesso.
fonte
Eu senti que nenhuma das respostas aqui explica por que gosto de iteradores como um conceito geral sobre indexação em contêineres. Observe que a maior parte da minha experiência no uso de iteradores não vem do C ++, mas de linguagens de programação de nível superior, como Python.
A interface do iterador impõe menos requisitos aos consumidores da sua função, o que permite que os consumidores façam mais com ela.
Se tudo que você precisa é ser capaz de transmitir-iterate, o desenvolvedor não se limita ao uso de recipientes indexáveis - eles podem usar qualquer classe de implementação
operator++(T&)
,operator*(T)
eoperator!=(const &T, const &T)
.Seu algoritmo funciona para o caso de você precisar - repetindo um vetor - mas também pode ser útil para aplicativos que você não necessariamente antecipa:
Tentar implementar um operador de colchetes que faça algo semelhante a esse iterador seria artificial, enquanto a implementação do iterador é relativamente simples. O operador de colchetes também traz implicações sobre os recursos de sua classe - que você pode indexar para qualquer ponto arbitrário - que podem ser difíceis ou ineficientes de implementar.
Os iteradores também se prestam à decoração . As pessoas podem escrever iteradores que utilizam um iterador em seu construtor e ampliam sua funcionalidade:
Embora esses brinquedos possam parecer comuns, não é difícil imaginar o uso de iteradores e decoradores de iteradores para fazer coisas poderosas com uma interface simples - decorando um iterador de encaminhamento somente dos resultados do banco de dados com um iterador que constrói um objeto de modelo a partir de um único resultado, por exemplo . Esses padrões permitem a iteração com eficiência de memória de conjuntos infinitos e, com um filtro como o que escrevi acima, uma avaliação potencialmente lenta dos resultados.
Parte do poder dos modelos C ++ é a sua interface do iterador, quando aplicada a tipos de matrizes C de comprimento fixo, decai para uma aritmética simples e eficiente do ponteiro , tornando-a uma abstração verdadeiramente de custo zero.
fonte