Setelah kita mengenal berbagai container di Section Standard Library, muncul pertanyaan alami: bagaimana cara algoritma seperti sort, find, atau count bekerja di atas container yang strukturnya sangat berbeda? Jawabannya adalah iterator, yaitu objek yang berperilaku mirip pointer untuk menelusuri elemen-elemen container satu per satu. Iterator menyembunyikan detail internal container di balik antarmuka kecil yang seragam, sehingga algoritma tidak perlu tahu apakah data disimpan berurutan di memori atau tersebar di node-node terpisah. Pada Chapter ini kita membedah apa itu iterator, antarmuka operasinya, dan membuktikan sendiri mengapa iterator membuat algoritma bisa dipakai ulang di semua container.
Iterator sebagai Generalisasi Pointer #
Cara paling mudah memahami iterator adalah dengan mengingat kembali pointer pada array C-style. Dengan pointer, kita bisa menunjuk satu elemen, maju dengan ++, membaca nilai yang ditunjuk dengan *, dan membandingkan dua pointer dengan ==. Iterator menawarkan operasi yang sama, tetapi tidak terbatas pada blok memori berurutan: std::list, std::map, dan std::set yang menyimpan data di node-node tersebar tetap bisa ditelusuri dengan sintaks yang identik.
Perbedaan pentingnya, iterator adalah abstraksi yang dibangun di atas struktur data, bukan alamat memori mentah. Untuk std::vector, iterator memang biasanya dibungkus dari pointer, tetapi untuk std::list iterator menyimpan pointer ke node list dan operator ++-nya berarti “pindah ke node berikutnya”, bukan “geser alamat sebanyak ukuran tipe”. Karena antarmukanya sama, kode pemakai tidak perlu berubah.
| Operasi | Makna | Analogi pointer |
|---|---|---|
*it | membaca (atau mengubah) elemen yang ditunjuk | *p |
it->anggota | mengakses member objek yang ditunjuk | p->anggota |
++it | maju ke elemen berikutnya | ++p |
it1 == it2 | memeriksa dua iterator menunjuk posisi yang sama | p1 == p2 |
it1 != it2 | kebalikan dari ==, dipakai sebagai kondisi berhenti loop | p1 != p2 |
Iterator Menjembatani Algoritma dan Container #
Kekuatan sesungguhnya muncul ketika kita menulis kode yang hanya bergantung pada antarmuka iterator, bukan pada tipe container. Program berikut memuat satu fungsi template jumlahNilai yang diuji terhadap std::vector sekaligus std::list — dua container yang penyimpanan internalnya sama sekali berbeda.
#include <iostream>
#include <list>
#include <string>
#include <vector>
// Fungsi template: bekerja untuk SEMUA container yang punya iterator
// dengan antarmuka begin(), end(), dereference (*), dan ++.
template <typename Container>
long jumlahNilai(const Container& c) {
long total = 0;
for (auto it = c.begin(); it != c.end(); ++it) {
total += *it; // dereference iterator: akses elemen yang ditunjuk
}
return total;
}
int main() {
std::vector<int> nilaiVector = {70, 85, 90, 60};
std::list<int> nilaiList = {70, 85, 90, 60};
// Fungsi yang sama dipakai untuk dua container berbeda
std::cout << "Total vector: " << jumlahNilai(nilaiVector) << "\n";
std::cout << "Total list : " << jumlahNilai(nilaiList) << "\n";
// Iterasi manual: iterator berperilaku seperti pointer
std::vector<std::string> nama = {"Budi", "Siti", "Dewi"};
for (auto it = nama.begin(); it != nama.end(); ++it) {
std::cout << *it << " (" << it->size() << " huruf)\n";
}
return 0;
}Output yang Dihasilkan:
Total vector: 305
Total list : 305
Budi (4 huruf)
Siti (4 huruf)
Dewi (4 huruf)Mari kita telaah baris demi baris:
- Baris 5-7 (
template <typename Container>): fungsijumlahNilaimenerima tipe container apa pun; compiler akan meng-instansiasikan fungsi ini untuk setiap tipe yang dipakai. - Baris 9 (
for (auto it = c.begin(); ...):c.begin()mengembalikan iterator yang menunjuk elemen pertama;automenyuruh compiler menuliskan tipe iterator yang panjang untuk kita. - Baris 9 (
it != c.end()):c.end()adalah iterator yang menandai akhir penelusuran; selamaitbelum mencapainya, loop berjalan. - Baris 10 (
total += *it): operator*membuka isi elemen yang ditunjuk iterator, sama seperti dereference pada pointer. - Baris 17-18: fungsi template yang sama dipanggil untuk
std::vectordanstd::list, dan keduanya menghasilkan total305. - Baris 22-24 (
it->size()): operator->pada iterator bekerja seperti->pada pointer, mengakses memberstd::stringyang ditunjuk.
Output membuktikan poin utamanya: angka total yang sama keluar dari container yang berbeda karena algoritma penjumlahan kita hanya berbicara lewat iterator. Inilah desain yang membuat seluruh keluarga algoritma <algorithm> — yang mulai kita pakai pada chapter-chapter berikutnya — bisa melayani semua container sekaligus.
Keterbatasan yang Perlu Diketahui #
Iterator bukan pointer sejati, sehingga tidak semua operasi pointer tersedia di semua iterator. Pada std::list, kita tidak bisa menulis it + 3 atau it - it, karena elemen list tidak berada di memori berurutan — operasi tersebut hanya sah pada iterator yang mendukung akses acak (random access). Untuk maju beberapa langkah pada list, kita memakai std::advance atau menulis ++it tiga kali.
Kondisi ini menuntun kita pada pertanyaan berikutnya: iterator ternyata dibagi ke dalam beberapa kategori kemampuan, dan setiap algoritma menyatakan kategori minimum yang ia butuhkan. Chapter berikutnya membahas lima kategori iterator tersebut beserta container yang memberikan masing-masing kategori.
Rujukan resmi: std::iterator overview di cppreference dan iterator tags .