Como navegar por um vetor usando iteradores? (C ++)

105

O objetivo é acessar o "enésimo" elemento de um vetor de strings em vez do operador [] ou do método "at". Pelo que entendi, os iteradores podem ser usados ​​para navegar por contêineres, mas nunca usei iteradores antes, e o que estou lendo é confuso.

Se alguém pudesse me dar algumas informações sobre como conseguir isso, eu agradeceria. Obrigado.

kevin
fonte
Os vetores não são exclusivos do STL de C ++? Vou editá-lo de qualquer maneira
Kevin,
kevin: vetor é um termo genérico que pode ser usado por qualquer linguagem, particularmente aquelas relacionadas à matemática como Mathematica ou Matlab.
Gabe,
@michael, sim haha ​​Eu editei após o comentário de Gabriel.
kevin

Respostas:

112

Você precisa usar o método begine endda vectorclasse, que retorna o iterador referindo-se ao primeiro e ao último elemento respectivamente.

using namespace std;  

vector<string> myvector;  // a vector of stings.


// push some strings in the vector.
myvector.push_back("a");
myvector.push_back("b");
myvector.push_back("c");
myvector.push_back("d");


vector<string>::iterator it;  // declare an iterator to a vector of strings
int n = 3;  // nth element to be found.
int i = 0;  // counter.

// now start at from the beginning
// and keep iterating over the element till you find
// nth element...or reach the end of vector.
for(it = myvector.begin(); it != myvector.end(); it++,i++ )    {
    // found nth element..print and break.
    if(i == n) {
        cout<< *it << endl;  // prints d.
        break;
    }
}

// other easier ways of doing the same.
// using operator[]
cout<<myvector[n]<<endl;  // prints d.

// using the at method
cout << myvector.at(n) << endl;  // prints d.
codadicto
fonte
5
Isso ignora o fato de que std::vectortem iteradores de acesso aleatório.
sbi
24
Independentemente de saber se o tipo de iterador é de acesso aleatório ou não, a "melhor" maneira de mover um iterador para frente n espaços não é escrever seu próprio loop, mas chamar std::advance(it, n). Ele é definido para fazer exatamente o que você deseja e será usado automaticamente it + nse o iterador estiver marcado como acesso aleatório, ou fará o loop se for necessário.
Steve Jessop,
61

Normalmente, os iteradores são usados ​​para acessar elementos de um contêiner de maneira linear; entretanto, com "iteradores de acesso aleatório", é possível acessar qualquer elemento da mesma maneira que operator[].

Para acessar elementos arbitrários em um vetor vec , você pode usar o seguinte:

vec.begin()                  // 1st
vec.begin()+1                // 2nd
// ...
vec.begin()+(i-1)            // ith
// ...
vec.begin()+(vec.size()-1)   // last

A seguir está um exemplo de um padrão de acesso típico (versões anteriores de C ++):

int sum = 0;
using Iter = std::vector<int>::const_iterator;
for (Iter it = vec.begin(); it!=vec.end(); ++it) {
    sum += *it;
}

A vantagem de usar o iterador é que você pode aplicar o mesmo padrão a outros contêineres :

sum = 0;
for (Iter it = lst.begin(); it!=lst.end(); ++it) {
    sum += *it;
}

Por esse motivo, é realmente fácil criar um código de modelo que funcione da mesma forma, independentemente do tipo de contêiner . Outra vantagem dos iteradores é que ele não assume que os dados residem na memória; por exemplo, pode-se criar um iterador direto que pode ler dados de um fluxo de entrada ou que simplesmente gera dados em tempo real (por exemplo, um intervalo ou gerador de números aleatórios).

Outra opção usando std::for_eache lambdas:

sum = 0;
std::for_each(vec.begin(), vec.end(), [&sum](int i) { sum += i; });

Desde C ++ 11, você pode usar autopara evitar a especificação de um nome de tipo muito longo e complicado do iterador como visto antes (ou ainda mais complexo):

sum = 0;
for (auto it = vec.begin(); it!=vec.end(); ++it) {
    sum += *it;
}

E, além disso, há uma variante mais simples para cada:

sum = 0;
for (auto value : vec) {
    sum += value;
}

E, finalmente, há também std::accumulateonde você deve ter cuidado ao adicionar números inteiros ou números de ponto flutuante.

Michael Aaron Safyan
fonte
53

Em C ++ - 11, você pode fazer:

std::vector<int> v = {0, 1, 2, 3, 4, 5};
for (auto i : v)
{
   // access by value, the type of i is int
   std::cout << i << ' ';
}
std::cout << '\n';

Veja aqui as variações: https://en.cppreference.com/w/cpp/language/range-for

Lashgar
fonte
4
POR QUE ISSO TEM GOSTOS ZERO ?! <3
jperl
3
@jperl Postado 8 anos atrasado. Levará os próximos 8 anos para conseguir
votos positivos
@jperl, bem, a resposta está fora do tópico. Embora esse recurso de loop seja bom, não ajuda você a saber quando está no enésimo elemento, que é a pergunta do OP. Além disso, qualquer resposta que requeira complexidade de tempo O (n), como esta, é muito ruim. Acessar o enésimo elemento de um vetor deve ser sempre O (1).
Elliott
@lashgar eu tentei isso com array mas falhou. Isso funciona para array?
era s'q
@ eras'q, tentei com gcc 7.5.0no Ubuntu 18.04 e funciona para array da mesma maneira.
lashgar
17

Os iteradores do Vector são iteradores de acesso aleatório, o que significa que eles se parecem e funcionam como ponteiros simples.

Você pode acessar o enésimo elemento adicionando n ao iterador retornado do begin()método do contêiner ou pode usar o operador [].

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

int sixth = *(it + 5);
int third = *(2 + it);
int second = it[1];

Alternativamente, você pode usar a função de avanço que funciona com todos os tipos de iteradores. (Você teria que considerar se realmente deseja realizar "acesso aleatório" com iteradores de acesso não aleatório, já que isso pode ser uma coisa cara de se fazer.)

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

std::advance(it, 5);
int sixth = *it;
UncleBens
fonte
1
Você também pode usar advancepara iteradores de acesso aleatório ou iteradores de categoria desconhecida, uma vez que é garantido que funcione em tempo constante nesse caso. É por isso que os iteradores definidos pelo usuário devem ser marcados corretamente.
Steve Jessop,
De fato, mas advanceé realmente irritante de usar (por causa do uso do parâmetro out) se você sabe que está lidando com iteradores de acesso aleatório. Só recomendaria em código genérico, e se não fosse muito usado (se o algoritmo não suportar bem iteradores de acesso não aleatório, que seja - por exemplo, std::sort poderia classificar um, std::listmas não porque seria ridiculamente ineficiente )
UncleBens
Claro, o exemplo clássico seria se seu algoritmo só realmente precisasse de um InputIterator, mas por alguma razão ele às vezes pula para a frente, então você deseja que seja mais eficiente se o iterador tiver acesso aleatório. Não vale a pena restringir seu algoritmo ao acesso aleatório apenas usando operator+. Mas a pergunta era explicitamente sobre vetor, então não há nada de errado com a primeira parte de sua resposta. Eu apenas pensei que a segunda parte poderia implicar "você não pode usar o advance com iteradores de acesso aleatório, mesmo se quiser" para alguém que nunca viu advanceantes.
Steve Jessop,
OK, reformulei esse bit e dei o exemplo com um vetor.
UncleBens
segunda linha, Vectordeve ser minúscula
Lei Yang
0

Aqui está um exemplo de acesso ao ithíndice de a std::vectorusando um std::iteratordentro de um loop que não requer o incremento de dois iteradores.

std::vector<std::string> strs = {"sigma" "alpha", "beta", "rho", "nova"};
int nth = 2;
std::vector<std::string>::iterator it;
for(it = strs.begin(); it != strs.end(); it++) {
    int ith = it - strs.begin();
    if(ith == nth) {
        printf("Iterator within  a for-loop: strs[%d] = %s\n", ith, (*it).c_str());
    }
}

Sem for-loop

it = strs.begin() + nth;
printf("Iterator without a for-loop: strs[%d] = %s\n", nth, (*it).c_str());

e usando o atmétodo:

printf("Using at position: strs[%d] = %s\n", nth, strs.at(nth).c_str());
hmofrad
fonte