Model dari bagian 1 sudah menetapkan apa yang disimulasikan; sekarang kita memutuskan dengan apa keadaan antrean disimpan. Keputusan struktur data pada proyek nyata jarang sesederhana memilih yang tercepat — ia menentukan seberapa banyak bug yang bisa masuk, seberapa mudah kode dibaca, dan seberapa jauh kita bisa mengembangkan program. Pada bagian ini kita mengulang singkat (recap) std::queue yang pernah dibahas pada chapter queue-dan-deque, melengkapinya dengan struct Teller, lalu membuktikan perilakunya dengan program kecil yang benar-benar kita jalankan.
Operasi yang Dibutuhkan Mesin Simulasi #
Mari kita daftar dulu operasi yang benar-benar dipakai event loop, karena daftar inilah yang menentukan pilihan struktur data:
| Operasi pada antrean | Kapan dipakai | Kebutuhan |
|---|---|---|
| Tambah pelanggan di belakang | saat kedatangan | cepat, selalu O(1) |
| Lihat pelanggan terdepan | teller siap melayani | tanpa menghapus |
| Hapus pelanggan terdepan | layanan dimulai | cepat, selalu O(1) |
| Tanya jumlah yang menunggu | pencatatan statistik per tick | murah |
| Tanya apakah kosong | syarat teller mengambil | murah |
Pola “tambah di satu ujung, ambil dari ujung lain” adalah definisi antrean FIFO, dan std::queue adalah wadah standar yang dikhususkan untuk pola ini. Dengan menyediakan hanya lima operasi di atas — tanpa akses acak, tanpa iterasi bebas — adaptor ini mencegah kita diam-diam “memotret” antrean di tengah, perilaku yang akan merusak makna model.
std::queue: Adaptor FIFO dari Standard Library #
std::queue bukan container berdiri sendiri, melainkan container adaptor: pembungkus tipis di atas container lain (secara bawaan std::deque) yang membatasi antarmuka menjadi FIFO saja. API yang kita pakai hanya enam function:
| Function | Perilaku |
|---|---|
push(x) | menyisipkan x di belakang antrean |
pop() | menghapus elemen terdepan, tanpa mengembalikannya |
front() | mengembalikan referensi elemen terdepan |
back() | mengembalikan referensi elemen terakhir |
size() | banyaknya elemen |
empty() | true bila antrean kosong |
Dua struct pelengkap model kita juga jadi di bagian ini: Teller yang mencatat kapan loket kembali bebas, dan antrean itu sendiri di dalam mesin simulasi:
struct Teller {
int id;
int bebasPada; // menit teller bebas melayani lagi
int jumlahDilayani; // banyak pelanggan yang sudah dilayani
};
// Di dalam jalankanSimulasi():
std::queue<Pelanggan> antrean;
Teller teller{1, 0, 0};bebasPada: satu field yang merangkum seluruh keadaan teller — bilatick >= bebasPada, loket siap menerima. Tidak perlu flagsedangSibukterpisah karena waktu bebas sudah mengandung informasi itu.jumlahDilayani: penghitung untuk perhitungan utilisasi di bagian 4; dilacak saat kejadian terjadi, bukan dihitung ulang dari riwayat di akhir.std::queue<Pelanggan> antrean: elemennya disalin (by value) ke dalam antrean;Pelangganberukuran kecil (empatint), sehingga salinan murah dan kita bebas dari kepusingan lifetime.
Mencoba Perilaku FIFO Secara Nyata #
Sebelum menyelimuti antrean dengan logika simulasi, kita buktikan dulu perilaku dasarnya dengan program kecil. Program ini mengantre tiga pelanggan bernama, memeriksa ujung antrean, lalu memanggil mereka satu per satu:
// demo-antrean.cpp — Bagian 2: std::queue sebagai antrean teller.
// Membuktikan perilaku FIFO: pelanggan yang datang lebih dulu dilayani lebih dulu.
#include <iostream>
#include <queue>
#include <string>
struct Pelanggan {
int id;
std::string nama;
};
int main() {
std::queue<Pelanggan> antrean;
// Tiga pelanggan datang; yang pertama masuk paling depan.
antrean.push(Pelanggan{1, "Budi"});
antrean.push(Pelanggan{2, "Siti"});
antrean.push(Pelanggan{3, "Dewi"});
std::cout << "Isi antrean: " << antrean.size() << " pelanggan\n";
std::cout << "Depan : p" << antrean.front().id << " ("
<< antrean.front().nama << ")\n";
std::cout << "Belakang: p" << antrean.back().id << " ("
<< antrean.back().nama << ")\n";
// Teller memanggil satu per satu dari depan.
while (!antrean.empty()) {
Pelanggan p = antrean.front();
antrean.pop(); // pop() menghapus, tetapi TIDAK mengembalikan nilai
std::cout << "Teller memanggil p" << p.id << " (" << p.nama
<< "), sisa antrean: " << antrean.size() << '\n';
}
std::cout << "Antrean kosong, teller istirahat.\n";
return 0;
}g++ -std=c++23 -Wall -Wextra demo-antrean.cpp -o demo-antrean
./demo-antreanOutput yang Dihasilkan:
Isi antrean: 3 pelanggan
Depan : p1 (Budi)
Belakang: p3 (Dewi)
Teller memanggil p1 (Budi), sisa antrean: 2
Teller memanggil p2 (Siti), sisa antrean: 1
Teller memanggil p3 (Dewi), sisa antrean: 0
Antrean kosong, teller istirahat.Perhatikan tiga hal dari output di atas. Pertama, urutan pemanggilan persis urutan kedatangan — inilah jaminan FIFO yang nantinya kita kunci sebagai assertion di harness. Kedua, loop while (!antrean.empty()) memakai pola aman front() lalu pop(); variabel p memegang salinan sehingga mencetak setelah pop() tetap sah. Ketiga, size() menurun dengan tertib dari 2 ke 0 — kalau suatu saat angka ini aneh, bug-nya ada di pola ambil-hapus kita, bukan di library.
std::queue atau Linked List Buatan Sendiri #
Chapter linked-list mengajarkan cara membangun antrean dari simpul-simpul yang dihubungkan pointer. Kapan kita memilih yang buatan sendiri?
| Aspek | std::queue | Linked list buatan sendiri |
|---|---|---|
| Kebenaran | teruji dan dijaga standar | seluruh risiko pointer ada pada kita |
| Bug khas | hampir tidak ada | memory leak, dangling pointer, lupa sisi belakang |
| Keterbacaan | tinggi, maksud langsung terlihat | perlu komentar dan disiplin |
| Kontrol memori | diserahkan ke adaptor | bebas: pool simpul, alokasi khusus |
| Peran belajar | pemakaian | memahami cara kerja di balik layar |
Untuk proyek ini kita memilih std::queue, dan pilihan itu adalah keputusan rekayasa, bukan kelembekan: tujuan simulator adalah statistik yang benar, bukan demonstrasi manajemen pointer. Linked list buatan sendiri tetap layak ketika kita butuh kontrol khusus — misalnya menghindari alokasi per-kedatangan pada simulasi jutaan entitas — dan bab memilih-struktur-data membahas trade-off tersebut lebih dalam.
Dengan struct Pelanggan, struct Teller, dan antrean yang perilakunya sudah terbukti, semua peran sudah terisi. Bagian berikutnya menyusunnya menjadi mesin simulasi yang benar-benar berjalan menit demi menit.