Bagaimana cara menavigasi melalui vektor menggunakan iterator? (C ++)

105

Tujuannya adalah untuk mengakses elemen "ke-n" dari vektor string alih-alih operator [] atau metode "at". Dari apa yang saya pahami, iterator dapat digunakan untuk menavigasi kontainer, tetapi saya belum pernah menggunakan iterator sebelumnya, dan apa yang saya baca membingungkan.

Jika ada yang bisa memberi saya beberapa informasi tentang bagaimana mencapai ini, saya akan sangat menghargainya. Terima kasih.

kevin
sumber
Bukankah vektor eksklusif untuk STL dari C ++? Saya akan mengeditnya
kevin
kevin: vektor adalah istilah umum yang dapat digunakan oleh semua bahasa, terutama yang berhubungan dengan matematika seperti Mathematica atau Matlab.
Gabe
@ michael, iya haha ​​saya edit setelah komentar gabe.
kevin

Jawaban:

112

Anda perlu menggunakan metode begindan kelas, yang mengembalikan iterator yang mengacu ke elemen pertama dan terakhir masing-masing.endvector

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.
codaddict
sumber
5
Ini merindukan fakta yang std::vectormemiliki iterator akses acak.
sbi
24
Terlepas dari apakah Anda tahu tipe iterator adalah akses-acak atau tidak, cara "terbaik" untuk memajukan iterator n spasi bukanlah dengan menulis loop Anda sendiri, tetapi untuk memanggil std::advance(it, n). Ini ditentukan untuk melakukan apa yang Anda inginkan, dan secara otomatis akan digunakan it + njika iterator diberi tag sebagai akses-acak, atau melakukan perulangan jika perlu.
Steve Jessop
61

Biasanya, iterator digunakan untuk mengakses elemen wadah secara linier; namun, dengan "pengatur akses acak", dimungkinkan untuk mengakses elemen apa pun dengan cara yang sama seperti operator[].

Untuk mengakses elemen arbitrer dalam vektor vec , Anda dapat menggunakan berikut ini:

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

Berikut ini adalah contoh pola akses umum (versi C ++ sebelumnya):

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

Keuntungan menggunakan iterator adalah Anda dapat menerapkan pola yang sama dengan penampung lain :

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

Karena alasan ini, sangat mudah untuk membuat kode template yang akan berfungsi sama apa pun jenis penampungnya . Keuntungan lain dari iterator adalah bahwa iterator tidak mengasumsikan data disimpan dalam memori; misalnya, seseorang dapat membuat iterator maju yang dapat membaca data dari aliran input, atau yang hanya menghasilkan data dengan cepat (misalnya generator rentang atau nomor acak).

Opsi lain menggunakan std::for_eachdan lambda:

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

Sejak C ++ 11 Anda dapat menggunakan autountuk menghindari menentukan nama iterator yang sangat panjang dan rumit seperti yang terlihat sebelumnya (atau bahkan lebih kompleks):

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

Dan, sebagai tambahan, ada yang lebih sederhana untuk setiap varian:

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

Dan terakhir ada juga di std::accumulatemana Anda harus berhati-hati apakah Anda menambahkan bilangan bulat atau floating point.

Michael Aaron Safyan
sumber
53

Di C ++ - 11 Anda dapat melakukan:

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';

Lihat di sini untuk variasi: https://en.cppreference.com/w/cpp/language/range-for

lashgar
sumber
4
MENGAPA INI TIDAK SUKA ?! <3
jperl
3
@jperl Diposting 8 tahun terlambat. Akan memakan waktu 8 tahun ke depan untuk mendapatkan cukup
suara positif
@jperl, nah jawabannya di luar topik. Meskipun fitur loop ini bagus, itu tidak membantu Anda untuk mengetahui kapan Anda berada di elemen ke-n, yang merupakan pertanyaan OP. Juga, jawaban apa pun yang membutuhkan kompleksitas waktu O (n), seperti ini, sangat buruk. Mengakses elemen ke-n dari sebuah vektor harus selalu O (1).
Elliott
@lashgar Saya mencoba ini dengan array tetapi gagal. Apakah ini berfungsi untuk array?
era s'q
@ eras'q, mencoba dengan gcc 7.5.0di Ubuntu 18.04 dan berfungsi untuk array dengan cara yang sama.
lashgar
17

Iterator vektor adalah iterator akses acak yang berarti mereka terlihat dan terasa seperti pointer biasa.

Anda dapat mengakses elemen ke-n dengan menambahkan n ke iterator yang dikembalikan dari metode penampung begin(), atau Anda dapat menggunakan operator [].

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

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

Alternatifnya, Anda dapat menggunakan fungsi advance yang bekerja dengan semua jenis iterator. (Anda harus mempertimbangkan apakah Anda benar-benar ingin melakukan "akses acak" dengan iterator non-random-access, karena itu mungkin hal yang mahal untuk dilakukan.)

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

std::advance(it, 5);
int sixth = *it;
UncleBens
sumber
1
Anda juga dapat menggunakan advanceuntuk iterator akses acak, atau iterator dengan kategori tidak diketahui, karena dijamin beroperasi dalam waktu yang konstan dalam kasus tersebut. Inilah mengapa iterator yang ditentukan pengguna harus diberi tag dengan benar.
Steve Jessop
Memang, tetapi advancesangat mengganggu untuk digunakan (karena penggunaan parameter out) jika Anda tahu Anda berurusan dengan iterator akses acak. Hanya akan merekomendasikan dalam kode generik, dan jika tidak banyak digunakan (jika algoritme tidak mendukung iterator non-random-access dengan baik, biarlah - misalnya, std::sort dapat mengurutkan std::listtetapi tidak karena akan sangat tidak efisien ).
UncleBens
Tentu, contoh klasiknya adalah jika algoritme Anda hanya benar-benar membutuhkan InputIterator, tetapi karena alasan apa pun itu terkadang melompat ke depan, jadi Anda ingin lebih efisien jika iterator memiliki akses acak. Tidak ada gunanya membatasi algoritme Anda untuk akses acak hanya dengan menggunakan operator+. Tetapi pertanyaannya secara eksplisit tentang vektor, jadi tidak ada yang salah dengan bagian pertama dari jawaban Anda. Saya hanya berpikir bagian kedua mungkin menyiratkan "Anda tidak dapat menggunakan advance dengan iterator akses acak, bahkan jika Anda ingin" kepada seseorang yang belum pernah melihat advancesebelumnya.
Steve Jessop
Oke, tulis ulang bit itu dan berikan contoh dengan vektor.
UncleBens
baris kedua, Vectorharus huruf kecil
Lei Yang
0

Berikut adalah contoh mengakses ithindeks a std::vectormenggunakan std::iteratordalam loop yang tidak memerlukan penambahan dua iterator.

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());
    }
}

Tanpa for-loop

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

dan menggunakan atmetode:

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