Iterar através de um vetor C ++ usando um loop 'for'

140

Eu sou novo na linguagem C ++. Eu comecei a usar vetores e notei que em todo o código que vejo para iterar por um vetor por índices, o primeiro parâmetro do forloop é sempre algo baseado no vetor. Em Java, eu poderia fazer algo assim com um ArrayList:

for(int i=0; i < vector.size(); i++){
   vector[i].doSomething();
}

Existe uma razão para eu não ver isso em C ++? É uma má prática?

Flynn
fonte
1
O loop for não é uma função, portanto não possui parâmetros (ou argumentos, que é o que você passa). Você quer dizer algo como std::vector<int>::size_type i = 0;, ou talvez std::vector<int>::iterator it = vector.begin();?
Chris #
Exatamente, todos os exemplos que vejo são escritos assim.
Flynn
4
Em Java, eu preferiria um loop for-each ou usaria iteradores. Praticamente o mesmo que C ++, embora sintaxe ligeiramente diferente.
Jesse Good #
2
possível duplicação de Por que usar iteradores em vez de índices de matriz?
Johnsyweb
10
A maioria das respostas aqui assume incorretamente o Q como: Qual é a melhor / mais curta maneira de iterar std::vector? , o Q real que está sendo perguntado aqui é: Existe algum motivo para eu não ver isso em C ++? É uma má prática? aka Por que sempre vejo código em C ++ que usa iteradores durante a iteração std::vector?
Alok

Respostas:

93

Existe alguma razão para eu não ver isso em C ++? É uma má prática?

Não. Não é uma prática ruim, mas a abordagem a seguir confere ao seu código certa flexibilidade .

Geralmente, antes do C ++ 11, o código para iterar sobre elementos de contêiner usa iteradores, algo como:

std::vector<int>::iterator it = vector.begin();

Isso ocorre porque torna o código mais flexível.

Todos os contêineres de biblioteca padrão suportam e fornecem iteradores. Se, posteriormente, você precisar mudar para outro contêiner, esse código não precisará ser alterado.

Nota: Escrever código que funcione com todos os contêineres de biblioteca padrão possíveis não é tão fácil quanto parece.

Alok Save
fonte
25
Alguém poderia me explicar por que, neste caso específico / snippet de código, você aconselha os iteradores sobre a indexação? Do que você está falando sobre essa "flexibilidade"? Pessoalmente, eu não gosto de iteradores, eles incham o código - simplesmente mais caracteres para digitar para o mesmo efeito. Especialmente se você não pode usar auto.
Violet Giraffe
8
@VioletGiraffe: Ao usar iteradores, é difícil dar errado em certos casos, como intervalos vazios, e o código é mais detalhado.
Alok Salvar
9
Por que você mostra apenas como declarar o iterador, mas não como usá-lo para executar o loop ...?
underscore_d
116

A razão pela qual você não vê essa prática é bastante subjetiva e não pode ter uma resposta definitiva, porque eu vi muitos dos códigos que usam sua maneira mencionada e não o iteratorestilo.

A seguir, pode haver razões pelas quais as pessoas não consideram a vector.size()maneira de fazer um loop:

  1. Ser paranóico em ligar size()todas as vezes na condição de loop. No entanto, não é um problema ou pode ser corrigido trivialmente
  2. Preferindo std::for_each()sobre o forpróprio loop
  3. Mais tarde, mudar o contêiner de std::vectorpara outro (por exemplo map, list) também exigirá a alteração do mecanismo de loop, porque nem todo contêiner suporta o size()estilo de loop

O C ++ 11 fornece uma boa facilidade para percorrer os contêineres. Isso é chamado de "intervalo baseado em loop" (ou "aprimorado para loop" em Java).

Com pouco código, você pode percorrer o total (obrigatório!) std::vector:

vector<int> vi;
...
for(int i : vi) 
  cout << "i = " << i << endl;
iammilind
fonte
12
Apenas para observar uma pequena desvantagem do intervalo baseado no loop : você não pode usá-lo com #pragma omp parallel for.
Liborm
2
Eu gosto da versão compacta porque há menos código para ler. Depois de fazer o ajuste mental, é muito mais fácil entender e os bugs se destacam mais. Isso também torna muito mais óbvio quando há uma iteração não padrão, porque há um pedaço muito maior de código.
Code Abominator
87

A maneira mais limpa de iterar através de um vetor é através de iteradores:

for (auto it = begin (vector); it != end (vector); ++it) {
    it->doSomething ();
}

ou (equivalente ao acima)

for (auto & element : vector) {
    element.doSomething ();
}

Antes do C ++ 0x, é necessário substituir auto pelo tipo de iterador e usar funções-membro em vez de funções globais começarem e terminarem.

Provavelmente é isso que você viu. Comparado à abordagem mencionada, a vantagem é que você não depende muito do tipo de vector. Se você mudar vectorpara uma classe "tipo de coleção" diferente, seu código provavelmente ainda funcionará. No entanto, você pode fazer algo semelhante em Java. Não há muita diferença conceitualmente; C ++, no entanto, usa modelos para implementar isso (em comparação com genéricos em Java); portanto, a abordagem irá funcionar para todos os tipos para os quais begine endfunções são definidas, mesmo para os tipos não-classe, tais como matrizes estáticos. Veja aqui: Como o intervalo de trabalho funciona para matrizes simples?

JohnB
fonte
5
automático, início / fim gratuito também são C ++ 11. E também, você deve usá-lo em vez de em muitos casos.
ForEveR #
Sim, você está certo. A implementação begine end, no entanto, é unilateral.
JohnB
O @JohnB é mais do que um liner, porque também funciona para matrizes de tamanho fixo. autopor outro lado, seria bastante complicado.
Juanchopanza
Se você precisar apenas para vetor, é uma linha.
JohnB
Ainda assim, o primeiro exemplo é enganador, pois não pode funcionar em C ++ 03, enquanto o seu fraseado sugere que sim.
Juanchopanza #
35

A maneira correta de fazer isso é:

for(std::vector<T>::iterator it = v.begin(); it != v.end(); ++it) {
    it->doSomething();
 }

Onde T é o tipo da classe dentro do vetor. Por exemplo, se a classe for CActivity, basta escrever CActivity em vez de T.

Esse tipo de método funcionará em todos os STL (não apenas nos vetores, o que é um pouco melhor).

Se você ainda deseja usar índices, o caminho é:

for(std::vector<T>::size_type i = 0; i != v.size(); i++) {
    v[i].doSomething();
}
DiGMi
fonte
nem std::vector<T>::size_typesempre size_t? Esse é o tipo que eu sempre uso para isso.
Violet Giraffe
1
@VioletGiraffe Tenho certeza de que você está certo (realmente não verificou), mas é uma prática melhor usar std :: vector <T> :: size_type.
DiGMi
8

Existem algumas razões fortes para usar iteradores, alguns dos quais são mencionados aqui:

A troca de contêineres posteriormente não invalida seu código.

ou seja, se você passar de um vetor std :: para uma lista std :: ou std :: set, não poderá usar índices numéricos para obter o valor contido. O uso de um iterador ainda é válido.

Captura em tempo de execução de iteração inválida

Se você modificar seu contêiner no meio do seu loop, na próxima vez em que usar seu iterador, ele lançará uma exceção de iterador inválida.

Eddie Parker
fonte
1
você poderia apontar para algum artigo / post que explica os pontos acima com código de exemplo? seria ótimo! ou se você poderia adicionar um :)
Anu
5

Fiquei surpreso que ninguém mencionou que a iteração através de uma matriz com um índice inteiro facilita a gravação de código defeituoso ao inscrever uma matriz com o índice errado. Por exemplo, se você tiver aninhado loops usando ie jcomo índices, poderá subscrever incorretamente uma matriz com je não ie assim introduzir uma falha no programa.

Por outro lado, as outras formas listadas aqui, a saber, o forloop baseado em intervalo e os iteradores, são muito menos propensos a erros. A semântica da linguagem e o mecanismo de verificação de tipo do compilador impedirão que você acesse acidentalmente uma matriz usando o índice errado.

Diomidis Spinellis
fonte
4

Com o STL, os programadores usam iteratorspara percorrer contêineres, já que o iterador é um conceito abstrato, implementado em todos os contêineres padrão. Por exemplo, std::listnão tem nada operator [].

Para sempre
fonte
3

O uso do operador automático facilita o uso, pois não é necessário se preocupar com o tipo de dados e o tamanho do vetor ou qualquer outra estrutura de dados.

Iterando vetor usando auto e for loop

vector<int> vec = {1,2,3,4,5}

for(auto itr : vec)
    cout << itr << " ";

Resultado:

1 2 3 4 5

Você também pode usar esse método para iterar conjuntos e listar. O uso de auto detecta automaticamente o tipo de dados usado no modelo e permite usá-lo. Assim, mesmo se tivéssemos uma vectorde stringou chara mesma sintaxe vai funcionar muito bem

Hrishikesh
fonte
1

A maneira correta de iterar o loop e imprimir seus valores é a seguinte:

#include<vector>

//declare the vector of type int
vector<int> v;

//insert the 5 element in the vector
for ( unsigned int i = 0; i < 5; i++){
    v.push_back(i);
}

//print those element
for (auto it = 0; it < v.end(); i++){
    std::cout << *it << std::endl;
}
Nikhil Rai
fonte
1

Aqui está uma maneira mais simples de iterar e imprimir valores em vetor.

for(int x: A) // for integer x in vector A
    cout<< x <<" "; 
Akram Mohammed
fonte
0
 //different declaration type
    vector<int>v;  
    vector<int>v2(5,30); //size is 5 and fill up with 30
    vector<int>v3={10,20,30};
    
    //From C++11 and onwards
    for(auto itr:v2)
        cout<<"\n"<<itr;
     
     //(pre c++11)   
    for(auto itr=v3.begin(); itr !=v3.end(); itr++)
        cout<<"\n"<<*itr;
bashar
fonte