Iterasi melalui Vektor C ++ menggunakan loop 'for'

156

Saya baru mengenal bahasa C ++. Saya telah mulai menggunakan vektor, dan telah memperhatikan bahwa di semua kode yang saya lihat untuk iterasi meskipun vektor melalui indeks, parameter pertama dari forloop selalu sesuatu berdasarkan vektor. Di Java saya mungkin melakukan sesuatu seperti ini dengan ArrayList:

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

Apakah ada alasan mengapa saya tidak melihat ini di C ++? Apakah ini praktik yang buruk?

Flynn
sumber
1
Perulangan for bukanlah sebuah fungsi, jadi ia tidak memiliki parameter (atau argumen, yang Anda berikan). Apakah maksud Anda sesuatu seperti std::vector<int>::size_type i = 0;, atau mungkin std::vector<int>::iterator it = vector.begin();?
chris
Tepatnya, semua contoh yang saya lihat ditulis seperti itu.
Flynn
4
Di Java, saya lebih memilih for-each loop atau menggunakan iterator. Hampir sama dengan C ++ meskipun sintaksnya sedikit berbeda.
Jesse Good
2
kemungkinan duplikat dari Mengapa menggunakan iterator daripada indeks array?
Johnsyweb
10
Sebagian besar jawaban di sini salah mengasumsikan Q sebagai: Apa cara terbaik / terpendek untuk mengulang std::vector? , sebenarnya Q yang ditanyakan di sini adalah: Apakah ada alasan mengapa saya tidak melihat ini di C ++? Apakah ini praktik yang buruk? alias Mengapa saya selalu melihat kode dalam C ++ yang menggunakan iterator saat melakukan iterasi std::vector?
Alok Save

Jawaban:

106

Apakah ada alasan mengapa saya tidak melihat ini di C ++? Apakah ini praktik yang buruk?

Tidak. Ini bukan praktik yang buruk, tetapi pendekatan berikut membuat kode Anda memiliki fleksibilitas tertentu .

Biasanya, kode pra-C ++ 11 untuk iterasi elemen kontainer menggunakan iterator, seperti:

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

Ini karena membuat kode lebih fleksibel.

Semua wadah perpustakaan standar mendukung dan menyediakan iterator. Jika di kemudian hari Anda perlu beralih ke penampung lain, kode ini tidak perlu diubah.

Catatan: Menulis kode yang berfungsi dengan setiap kemungkinan penampung pustaka standar tidak semudah kelihatannya.

Alok Save
sumber
25
Adakah yang bisa menjelaskan mengapa dalam cuplikan kasus / kode ini Anda menyarankan iterator atas pengindeksan? Apa "fleksibilitas" yang Anda bicarakan ini? Secara pribadi, saya tidak suka iterator, mereka membengkak kode - hanya lebih banyak karakter untuk diketik untuk efek yang sama. Apalagi jika Anda tidak bisa menggunakan auto.
Violet Giraffe
8
@VioletGiraffe: Saat menggunakan iterator, sulit untuk salah dengan kasus-kasus tertentu seperti rentang kosong dan kodenya lebih bertele-tele. Tentu saja itu masalah atau persepsi dan pilihan, Jadi bisa diperdebatkan tanpa henti.
Alok Save
10
Mengapa Anda hanya menunjukkan cara mendeklarasikan iterator tetapi tidak menunjukkan cara menggunakannya untuk melakukan perulangan ...?
underscore_d
125

Alasan mengapa Anda tidak melihat praktik semacam itu cukup subjektif dan tidak dapat memiliki jawaban yang pasti, karena saya telah melihat banyak kode yang menggunakan cara yang Anda sebutkan daripada iteratorkode gaya.

Berikut ini bisa menjadi alasan orang tidak mempertimbangkan vector.size()cara perulangan:

  1. Menjadi paranoid tentang menelepon size()setiap kali dalam kondisi loop. Namun entah itu bukan masalah atau bisa diperbaiki sepele
  2. Lebih memilih std::for_each()atas forlingkaran itu sendiri
  3. Nanti mengubah wadah dari yang std::vectorlain (misalnya map, list) juga akan menuntut perubahan mekanisme perulangan, karena tidak setiap wadah mendukung size()gaya perulangan

C ++ 11 menyediakan fasilitas yang baik untuk bergerak melalui kontainer. Itu disebut "range based for loop" (atau "Enhanced for loop" di Java).

Dengan sedikit kode Anda dapat melintasi sepenuhnya (wajib!) std::vector:

vector<int> vi;
...
for(int i : vi) 
  cout << "i = " << i << endl;
iammilind
sumber
13
Hanya untuk mencatat kerugian kecil dari range based for loop : Anda tidak dapat menggunakannya dengan #pragma omp parallel for.
liborm
2
Saya suka versi ringkasnya karena lebih sedikit kode untuk dibaca. Setelah Anda membuat penyesuaian mental, jauh lebih mudah untuk memahami dan bug lebih menonjol. Itu juga membuatnya lebih jelas ketika ada iterasi non-standar yang terjadi karena ada potongan kode yang jauh lebih besar.
Code Abominator
94

Cara paling bersih untuk melakukan iterasi melalui vektor adalah melalui iterator:

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

atau (setara dengan yang di atas)

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

Sebelum C ++ 0x, Anda harus mengganti auto dengan tipe iterator dan menggunakan fungsi anggota alih-alih fungsi global dimulai dan diakhiri.

Ini mungkin yang Anda lihat. Dibandingkan dengan pendekatan yang Anda sebutkan, keuntungannya adalah Anda tidak terlalu bergantung pada jenisnya vector. Jika Anda mengubah vectorke kelas "tipe-koleksi" yang berbeda, kode Anda mungkin masih berfungsi. Namun, Anda juga dapat melakukan hal serupa di Java. Tidak banyak perbedaan secara konseptual; C ++, bagaimanapun, menggunakan template untuk mengimplementasikan ini (dibandingkan dengan generik di Java); karenanya pendekatan ini akan bekerja untuk semua tipe yang begindan endfungsinya ditentukan, bahkan untuk tipe non-kelas seperti array statis. Lihat di sini: Bagaimana cara kerja berbasis rentang untuk array biasa?

JohnB
sumber
5
otomatis, mulai / akhir gratis juga C ++ 11. Dan juga, Anda harus menggunakan ++ itu, daripada itu ++ dalam banyak kasus.
ForEveR
Ya kau benar. Menerapkan begindan end, bagaimanapun, adalah satu baris.
JohnB
@JohnB itu lebih dari satu-liner, karena bekerja untuk array ukuran tetap juga. autodi sisi lain akan cukup rumit.
juanchopanza
Jika Anda membutuhkannya hanya untuk vektor, itu adalah satu-liner.
JohnB
Namun, contoh pertama menyesatkan, karena tidak dapat berfungsi di C ++ 03, sedangkan ungkapan Anda menunjukkan bahwa itu berfungsi.
juanchopanza
35

Cara yang benar untuk melakukannya adalah:

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

Dimana T adalah tipe kelas di dalam vektor. Misalnya jika kelasnya adalah CActivity, tulis saja CActivity, bukan T.

Jenis metode ini akan bekerja pada setiap STL (Tidak hanya vektor, yang sedikit lebih baik).

Jika Anda masih ingin menggunakan indeks, caranya adalah:

for(std::vector<T>::size_type i = 0; i != v.size(); i++) {
    v[i].doSomething();
}
DiGMi
sumber
tidak std::vector<T>::size_typeselalu size_t? Itulah tipe yang selalu saya gunakan untuk itu.
Violet Giraffe
1
@VioletGiraffe Saya cukup yakin Anda benar (belum benar-benar diperiksa), tetapi praktik yang lebih baik adalah menggunakan std :: vector <T> :: size_type.
DiGMi
8

Ada beberapa alasan kuat untuk menggunakan iterator, beberapa di antaranya disebutkan di sini:

Beralih penampung nanti tidak membuat kode Anda tidak valid.

misalnya, jika Anda beralih dari std :: vector ke std :: list, atau std :: set, Anda tidak dapat menggunakan indeks numerik untuk mendapatkan nilai yang Anda masukkan. Penggunaan iterator masih valid.

Penangkapan waktu proses dari iterasi yang tidak valid

Jika Anda memodifikasi penampung Anda di tengah-tengah loop Anda, saat Anda menggunakan iterator Anda, itu akan memunculkan pengecualian iterator yang tidak valid.

Eddie Parker
sumber
1
Bisakah Anda menunjukkan beberapa artikel / posting kami yang menjelaskan poin-poin di atas dengan kode contoh? akan menjadi luar biasa! atau jika Anda dapat menambahkan satu :)
Anu
5

Saya terkejut tidak ada yang menyebutkan bahwa iterasi melalui array dengan indeks integer memudahkan Anda untuk menulis kode yang salah dengan memasukkan array dengan indeks yang salah. Misalnya, jika Anda memiliki loop bersarang yang menggunakan idan jsebagai indeks, Anda mungkin salah meng-subskrip sebuah array dengan jdaripada idan dengan demikian memperkenalkan kesalahan ke dalam program.

Sebaliknya, bentuk lain yang tercantum di sini, yaitu forloop berbasis rentang , dan iterator, jauh lebih rentan terhadap kesalahan. Semantik bahasa dan mekanisme pemeriksaan jenis kompiler akan mencegah Anda mengakses larik secara tidak sengaja menggunakan indeks yang salah.

Diomidis Spinellis
sumber
4

Dengan STL, pemrogram menggunakan iteratorsuntuk melintasi melalui kontainer, karena iterator adalah konsep abstrak, diimplementasikan di semua kontainer standar. Misalnya std::listtidak punya operator []sama sekali.

Selama-lamanya
sumber
4

Menggunakan operator otomatis sangat memudahkan penggunaan karena seseorang tidak perlu khawatir tentang tipe data dan ukuran vektor atau struktur data lainnya.

Iterasi vektor menggunakan auto dan for loop

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

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

Keluaran:

1 2 3 4 5

Anda juga dapat menggunakan metode ini untuk mengulang set dan daftar. Menggunakan auto secara otomatis mendeteksi tipe data yang digunakan dalam template dan memungkinkan Anda menggunakannya. Jadi, meskipun kita memiliki salah vectorsatu stringatau charsintaks yang sama akan bekerja dengan baik

Hrishikesh
sumber
3

Cara yang benar untuk mengulang loop dan mencetak nilainya adalah sebagai berikut:

#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
sumber
2

Berikut adalah cara yang lebih sederhana untuk mengulang dan mencetak nilai dalam vektor.

for(int x: A) // for integer x in vector A
    cout<< x <<" "; 
Akram Mohammed
sumber
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
sumber
-1

Jika Anda menggunakan

std::vector<std::reference_wrapper<std::string>> names{ };

Jangan lupa, ketika Anda menggunakan auto in the for loop, untuk menggunakan juga get, seperti ini:

for (auto element in : names)
{
    element.get()//do something
}
rampok
sumber
Itu bukan sintaks yang valid.
John Doe