Iterator adalah jenderal dari pointer C++: objek yang bisa dereference (*it), di-increment (++it), dan dibandingkan (it != end). Container STL menyediakan iterator dengan kemampuan berbeda — dari yang hanya bisa maju (forward_list) hingga random-access (vector). Banyak algoritma STL (std::sort, std::copy, std::find) ditulis generik berbasis iterator, sehingga satu algoritma bekerja untuk semua container yang kategorinya cocok. Pada Chapter ini kita akan memetakan lima kategori iterator, operasi advance/next/prev, serta aturan iterator invalidation yang krusial untuk kode yang benar.
Lima Kategori Iterator #
| Kategori | Operasi yang didukung | Container STL |
|---|---|---|
| Input | *it (read), ++it, ==/!= | istream_iterator |
| Output | *it = v (write), ++it | back_inserter, ostream_iterator |
| Forward | input + multi-pass + default-constructible | forward_list, unordered_* |
| Bidirectional | forward + --it | list, set, map, multiset, multimap |
| Random Access | bidirectional + it[i], it + n, it - it2 | vector, deque, array, std::string |
Algoritma STL mensyaratkan kategori tertentu — std::sort butuh random-access, std::reverse butuh bidirectional, std::find cukup input.
Mekanisme: begin/end dan Ranges #
Setiap container menyediakan pasangan fungsi:
begin()/end(): iterator maju, denganendadalah past-the-end (satu setelah elemen terakhir).endtidak boleh di-dereference.rbegin()/rend(): reverse iterator — mengiterasi dari belakang ke depan.cbegin()/cend(): versiconst_iterator— elemen read-only.
Fungsi bebas std::begin(c) dan std::end(c) bekerja untuk array C-style juga, bukan hanya container STL.
Kode: Operasi Iterator Umum #
#include <iostream>
#include <vector>
#include <list>
#include <forward_list>
#include <algorithm>
#include <iterator>
template <typename It>
void print_range(It first, It last, const char* label) {
std::cout << label;
for (; first != last; ++first) std::cout << *first << ' ';
std::cout << '\n';
}
int main() {
std::vector<int> v = {1, 2, 3, 4, 5};
// 1) begin/end, rbegin/rend
std::cout << "forward : ";
for (auto it = v.begin(); it != v.end(); ++it) std::cout << *it << ' ';
std::cout << '\n';
std::cout << "reverse : ";
for (auto it = v.rbegin(); it != v.rend(); ++it) std::cout << *it << ' ';
std::cout << '\n';
// 2) cbegin/cend — const_iterator
auto cit = v.cbegin();
// *cit = 99; // ERROR — const iterator, elemen read-only
std::cout << "cbegin : " << *cit << '\n';
// 3) advance, distance, next, prev
auto it = v.begin();
std::advance(it, 3); // pindah 3 langkah maju
std::cout << "*advance(begin, 3) = " << *it << '\n';
std::cout << "distance(begin, it) = " << std::distance(v.begin(), it) << '\n';
std::cout << "*prev(it) = " << *std::prev(it) << '\n';
std::cout << "*next(it, -1) = " << *std::next(it, -1) << '\n';
// 4) Iterator kategori berbeda
std::list<int> L = {1, 2, 3, 4, 5};
auto lit = L.begin();
std::advance(lit, 2); // list punya bidirectional — advance O(n) di sini O(2)
std::cout << "list advance(begin, 2) = " << *lit << '\n';
std::forward_list<int> fl = {1, 2, 3, 4, 5};
auto fit = fl.begin();
std::advance(fit, 2); // forward_list hanya forward iterator
std::cout << "forward_list advance(begin, 2) = " << *fit << '\n';
// 5) inserter / back_inserter — output iterator
std::vector<int> dst;
std::copy(v.begin(), v.end(), std::back_inserter(dst));
print_range(dst.begin(), dst.end(), "dst : ");
// 6) Iterator invalidation — vektor setelah insert
std::vector<int> w = {1, 2, 3};
auto wit = w.begin();
w.push_back(4); // kemungkinan realokasi -> wit mungkin invalid
(void)wit; // sengaja tidak dibaca: mungkin dangling
std::cout << "w setelah push_back: ";
for (int x : w) std::cout << x << ' ';
std::cout << '\n';
return 0;
}Output yang Dihasilkan:
forward : 1 2 3 4 5
reverse : 5 4 3 2 1
cbegin : 1
*advance(begin, 3) = 4
distance(begin, it) = 3
*prev(it) = 3
*next(it, -1) = 3
list advance(begin, 2) = 3
forward_list advance(begin, 2) = 3
dst : 1 2 3 4 5
w setelah push_back: 1 2 3 4 Bedah Kode Baris-per-Baris #
v.begin()/v.end():beginmengembalikan iterator ke elemen pertama;endadalah past-the-end — bukan elemen valid. Loopfor (it = begin; it != end; ++it)adalah idiom iterasi.v.rbegin()/v.rend(): reverse iterator. Increment maju mundur — dari elemen terakhir ke pertama. Output5 4 3 2 1untuk vector{1,2,3,4,5}.v.cbegin(): mengembalikanconst_iterator. Mencoba*cit = 99gagal kompilasi — elemen read-only. Gunakan bila container tidak boleh dimutasi (mis. parameter fungsiconst std::vector<T>&).std::advance(it, 3): memajukan iteratornlangkah. Pada random-access iterator (vector), operasi iniO(1)(pointer arithmetic). Pada forward/bidirectional iterator, ia memanggil++itsebanyaknkali —O(n).std::distance(begin, it): jumlah increment yang diperlukan untuk mencapaiitdaribegin. Pada random-access iteratorO(1)(pengurangan pointer); pada kategori lainO(n).std::prev(it)/std::next(it): mengembalikan iterator baru yang telah digeser — tidak mengubah iterator asli.next(it, -1)setara denganprev(it)untuk random-access iterator.std::advance(lit, 2)padastd::list: list adalah bidirectional iterator —advancememanggil++itdua kali. TetapO(n)walau n kecil di sini.std::forward_list::advance: forward_list hanya punya forward iterator.advancehanya bisa maju (atau mundur dengannnegatif pada random-access — pada forward_list,nnegatif adalah compile error).std::back_inserter(dst): mengembalikan output iterator yang, saat ditulis*it = x, memanggildst.push_back(x). Pola idiomatik untuk algoritma yang menulis ke container.w.push_back(4)setelahauto wit = w.begin(): bila vector harus realokasi,witmenjadi dangling. Walaupun tidak crash di sini, mengakses*witsetelah push adalah undefined behavior. Aturan praktis: anggap semua iterator vector invalid setelah operasi yang mungkin realokasi (insert, push_back, resize).
Aturan Iterator Invalidation #
Setiap container memiliki aturan berbeda kapan iterator valid setelah operasi mutasi:
| Container | insert/push_back | erase |
|---|---|---|
vector | Invalidasi semua bila realokasi; bila tidak, dari titik insert ke end | Invalidasi dari titik erase ke end |
deque | Invalidasi semua iterator | Invalidasi semua iterator |
list/forward_list | Iterator tetap valid (kecuali yang di-erase) | Hanya iterator ke elemen yang dihapus yang invalid |
set/map | Iterator tetap valid | Hanya iterator ke elemen yang dihapus |
unordered_* | Rehash invalidasi semua iterator | Hanya iterator ke elemen yang dihapus |
Inserter Iterator #
Tiga jenis inserter untuk output iterator:
std::back_inserter(c)→ memanggilc.push_back(x).std::front_inserter(c)→ memanggilc.push_front(x)(butuhpush_front— tidak ada di vector).std::inserter(c, it)→ memanggilc.insert(it, x).
Ketiganya memudahkan komposisi dengan algoritma STL:
std::copy(src.begin(), src.end(), std::back_inserter(dst)); // append ke dst
std::transform(src.begin(), src.end(), std::back_inserter(dst),
[](int x) { return x * 2; }); // dst = 2*src
Ringkasan #
- Lima kategori iterator: input, output, forward, bidirectional, random-access — menentukan algoritma mana yang bisa dipakai.
begin/end,rbegin/rend,cbegin/cendmenyediakan iterasi maju, mundur, dan const.std::advance,std::distance,std::next,std::prev— operasi generik yang efisien untuk kategori tinggi,O(n)untuk kategori rendah.- Aturan invalidation berbeda per container — paling ketat pada
deque(semua invalid), paling longgar padalist(hanya yang di-erase). back_inserter/front_inserter/inserteradalah idiom output iterator untuk komposisi dengan algoritma STL.