Apa perbedaan antara const_iterator dan non-const iterator di C ++ STL?

146

Apa perbedaan antara a const_iteratordan an iteratordan di mana Anda akan menggunakan salah satunya?

Konrad
sumber
2
Nah, nama const_iterator terdengar seperti iterator adalah const, sedangkan hal yang ditunjukkan oleh iterator ini adalah const sebenarnya.
talekeDskobeDa

Jawaban:

131

const_iterators tidak mengizinkan Anda untuk mengubah nilai yang mereka tunjuk, iterators biasa .

Seperti semua hal di C ++, selalu lebih disukai const, kecuali ada alasan bagus untuk menggunakan iterator biasa (yaitu Anda ingin menggunakan fakta bahwa mereka tidak constmengubah nilai menunjuk ke).

Dominic Rodger
sumber
9
Di dunia yang sempurna, itulah masalahnya. Tetapi dengan C ++ const hanya sebaik orang yang menulis kode :(
JaredPar
bisa berubah ada untuk alasan yang sangat bagus. Ini jarang digunakan, dan melihat Google Code Search, tampaknya ada persentase yang wajar dari penggunaan yang valid. Kata kunci adalah alat pengoptimalan yang sangat ampuh, dan tidak seperti menghapusnya akan meningkatkan ketepatan konstanta ( batuk
batuk
2
Lebih seperti peretasan yang kuat. Dari semua contoh yang pernah saya lihat tentang penggunaan kata kunci 'bisa berubah', semua kecuali satu adalah indikator akurat bahwa kode itu ditulis dengan buruk dan perlu diubah sebagai peretasan untuk mengatasi cacat.
John Dibling
7
Itu memang memiliki penggunaan yang sah, seperti menyimpan hasil kalkulasi panjang dalam kelas const. Di sisi lain, itulah satu-satunya saat saya menggunakan mutable dalam hampir dua puluh tahun pengembangan C ++.
Kepala Geek
Contoh umum untuk menggunakan const_iterator adalah jika iteratornya adalah rvalue
talekeDskobeDa
41

Mereka seharusnya sudah cukup jelas. Jika iterator menunjuk ke elemen tipe T, maka const_iterator menunjuk ke elemen tipe 'const T'.

Ini pada dasarnya setara dengan jenis penunjuk:

T* // A non-const iterator to a non-const element. Corresponds to std::vector<T>::iterator
T* const // A const iterator to a non-const element. Corresponds to const std::vector<T>::iterator
const T* // A non-const iterator to a const element. Corresponds to std::vector<T>::const_iterator

Iterator const selalu menunjuk ke elemen yang sama, jadi iterator itu sendiri adalah const. Tetapi elemen yang dituju tidak harus konstan, jadi elemen yang dituju dapat diubah. Const_iterator adalah iterator yang menunjuk ke elemen const, jadi meskipun iterator itu sendiri dapat diperbarui (bertambah atau dikurangi, misalnya), elemen yang ditunjuknya tidak dapat diubah.

jalf
sumber
1
"Sebuah const iterator selalu menunjuk ke elemen yang sama," Ini tidak benar.
John Dibling
2
Bagaimana? Perhatikan garis bawah yang hilang. Saya membandingkan variabel tipe const std :: vector <T> :: iterator dengan std :: vector <T> :: const_iterator. Dalam kasus sebelumnya, iterator itu sendiri adalah const, sehingga tidak dapat dimodifikasi, tetapi elemen yang direferensikannya dapat dimodifikasi secara bebas.
jalf
4
Ah, begitu. Ya, saya melewatkan garis bawah yang hilang.
John Dibling
3
@JohnDibling Suara positif untuk menjelaskan seluk-beluk antara const iteraterdan const_iterator.
legends2k
8

Sayangnya, banyak metode untuk kontainer STL yang menggunakan iterator daripada const_iterators sebagai parameter. Jadi jika Anda memiliki sebuah const_iterator , Anda tidak dapat mengatakan "masukkan elemen sebelum elemen yang ditunjukkan oleh iterator ini" (mengatakan hal seperti itu secara konseptual bukanlah pelanggaran const, menurut saya). Jika Anda tetap ingin melakukannya, Anda harus mengubahnya menjadi iterator non-const menggunakan std :: advance () atau boost :: next () . Misalnya. boost :: next (container.begin (), std :: distance (container.begin (), the_const_iterator_we_want_to_unconst)) . Jika container adalah std :: list , maka waktu berjalan untuk panggilan tersebut adalah O (n) .

Jadi aturan universal untuk menambahkan const di mana pun "logis" untuk melakukannya, kurang universal dalam hal penampung STL.

Namun, penampung boost mengambil const_iterators (mis. Boost :: unordered_map :: erase ()). Jadi, saat Anda menggunakan penampung peningkat, Anda bisa menjadi "konstan agresif". Ngomong-ngomong, apakah ada yang tahu jika atau kapan kontainer STL akan diperbaiki?

Magnus Andermo
sumber
1
Ini mungkin masalah opini. Dalam kasus vectordan deque, memasukkan satu elemen membatalkan semua iterator yang ada, yang sebenarnya tidak terlalu const. Tapi saya mengerti maksud Anda. Operasi semacam itu dilindungi oleh container const-ness, bukan iterator. Dan saya bertanya-tanya mengapa tidak ada fungsi konversi iterator const-to-nonconst di antarmuka penampung standar.
Potatoswatter
Anda benar Potatoswatter, saya kategoris, itu adalah masalah pendapat untuk wadah akses acak dan container.begin () + (the_const_iterator_we_want_to_unconst - container.begin ()) adalah O (1) pula. Saya juga bertanya-tanya mengapa tidak ada fungsi konversi untuk wadah akses non-acak, tetapi mungkin ada alasan yang bagus? Apakah Anda tahu jika ada alasan mengapa fungsi untuk wadah akses non-acak tidak menggunakan const_iterators ?
Magnus Andermo
"Mengatakan hal seperti itu secara konseptual bukanlah pelanggaran konstitusi, menurut saya" - ini adalah komentar yang sangat menarik, saya memiliki pemikiran berikut tentang topik tersebut. Dengan petunjuk sederhana, seseorang dapat mengatakan int const * foo; int * const foo;dan int const * const foo;ketiganya valid dan berguna, masing-masing dengan caranya sendiri. std::vector<int> const barharus sama dengan yang kedua, tetapi sayangnya sering diperlakukan seperti yang ketiga. Akar penyebab masalahnya adalah kita tidak bisa mengatakan std::vector<int const> bar;kapan berarti tidak ada cara untuk mendapatkan efek yang sama seperti int const *foo;pada vektor.
dgnuff
6

Gunakan const_iterator kapan pun Anda bisa, gunakan iterator saat Anda tidak punya pilihan lain.

Naveen
sumber
4

Contoh minimal runnable

Iterator non-const memungkinkan Anda mengubah apa yang mereka tunjuk:

std::vector<int> v{0};
std::vector<int>::iterator it = v.begin();
*it = 1;
assert(v[0] == 1);

Iterator konstan tidak:

const std::vector<int> v{0};
std::vector<int>::const_iterator cit = v.begin();
// Compile time error: cannot modify container with const_iterator.
//*cit = 1;

Seperti ditunjukkan di atas, v.begin()adalah constkelebihan beban, dan kembali baik iteratoratau const_iteratortergantung pada const-ness variabel wadah:

Kasus umum di mana const_iteratorpops up adalah ketika thisdigunakan di dalam constmetode:

class C {
    public:
        std::vector<int> v;
        void f() const {
            std::vector<int>::const_iterator it = this->v.begin();
        }
        void g(std::vector<int>::const_iterator& it) {}
};

constmembuat thisconst, yang membuat this->vconst.

Anda biasanya dapat melupakannya dengan auto, tetapi jika Anda mulai meneruskan iterator tersebut, Anda perlu memikirkannya untuk tanda tangan metode.

Sama seperti const dan non-const, Anda dapat mengonversi dengan mudah dari non-const ke const, tetapi tidak sebaliknya:

std::vector<int> v{0};
std::vector<int>::iterator it = v.begin();

// non-const to const.
std::vector<int>::const_iterator cit = it;

// Compile time error: cannot modify container with const_iterator.
//*cit = 1;

// Compile time error: no conversion from const to no-const.
//it = ci1;

Yang mana yang akan digunakan: analogi dengan const intvs int: prefer const iterators kapan pun Anda dapat menggunakannya (saat Anda tidak perlu memodifikasi container dengannya), untuk mendokumentasikan niat Anda membaca dengan lebih baik tanpa memodifikasi.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
sumber
0

(seperti yang dikatakan orang lain) const_iterator tidak mengizinkan Anda memodifikasi elemen yang ditunjuknya, ini berguna di dalam metode kelas const. Ini juga memungkinkan Anda untuk mengekspresikan niat Anda.

Trey Jackson
sumber
0

ok Mari saya jelaskan dengan contoh yang sangat sederhana pertama tanpa menggunakan iterator konstan menganggap kami memiliki koleksi bilangan bulat acak koleksi "randomData"

    for(vector<int>::iterator i = randomData.begin() ; i != randomData.end() ; ++i)*i = 0;
for(vector<int>::const_iterator i = randomData.begin() ; i!= randomData.end() ; ++i)cout << *i;

Seperti yang dapat dilihat untuk menulis / mengedit data di dalam collection, iterator normal digunakan tetapi untuk tujuan membaca, iterator konstan telah digunakan. Jika Anda mencoba menggunakan iterator konstan di loop for pertama Anda akan mendapatkan kesalahan. Sebagai aturan praktis, gunakan iterator konstan untuk membaca data di dalam koleksi.

Tuan Coder
sumber