Setelah empat chapter hidup dengan kelemahan array C-style — tidak tahu ukurannya sendiri, meluruh jadi pointer, tidak bisa disalin — waktunya bertemu solusi standar: std::array. Tipe ini didefinisikan pada header <array> dan membungkus array C-style biasa dengan antarmuka modern. Dalam hal tata letak memori, std::array identik dengan array C-style: elemen-elemennya contiguous dan ukurannya tetap, sehingga kecepatannya tidak berkurang sama sekali — sifat zero-cost abstraction yang disebut pada Section Pengenalan C++. Pada Chapter ini kita mempelajari penggunaan std::array, perbedaan dua cara aksesnya (operator[] dan .at()), dan memetakan keunggulannya dibanding array C-style.
Penggunaan Dasar std::array #
Deklarasinya memakai dua parameter: tipe elemen dan ukuran, keduanya ditulis di dalam kurung siku tipe — std::array<int, 5>. Karena ukuran menjadi bagian dari tipe, compiler selalu tahu array ini sepanjang apa, di mana pun ia berada. Program berikut memperagakan anggota-anggota yang paling sering dipakai:
#include <array>
#include <iostream>
#include <stdexcept>
int main() {
std::array<int, 5> data = {3, 6, 9, 12, 15};
// ukuran melekat pada tipe: .size() selalu benar, tidak perlu sizeof
std::cout << "ukuran: " << data.size() << "\n";
// operator[] — cepat, tanpa pemeriksaan batas (sama seperti array C-style)
data[0] = 30;
std::cout << "data[0] setelah diubah: " << data[0] << "\n";
// .at() — mengakses dengan pemeriksaan batas
std::cout << "data.at(4) = " << data.at(4) << "\n";
// .at(10) berada di luar batas: exception dilempar dan kita tangkap
try {
std::cout << data.at(10) << "\n"; // baris ini tidak sempat tercetak
} catch (const std::out_of_range& e) {
std::cout << "Tertangkap: " << e.what() << "\n";
}
// fill: isi semua elemen dengan satu nilai yang sama
data.fill(7);
std::cout << "setelah fill(7) : ";
for (std::size_t i = 0; i < data.size(); ++i) {
std::cout << (i > 0 ? " " : "") << data[i];
}
std::cout << "\n";
// salin-assign antara dua std::array yang sama tipenya
std::array<int, 5> lain = {};
lain = data;
std::cout << "salinan berisi : " << lain.front() << " dan "
<< lain.back() << "\n";
return 0;
}Output yang Dihasilkan:
ukuran: 5
data[0] setelah diubah: 30
data.at(4) = 15
Tertangkap: array::at: __n (which is 10) >= _Nm (which is 5)
setelah fill(7) : 7 7 7 7 7
salinan berisi : 7 dan 7Mari kita bedah:
- Baris 8 (
std::array<int, 5> data = {3, 6, 9, 12, 15};): initializer list yang sama dengan array C-style berfungsi di sini, dan ukuran5ikut tertulis pada tipe. Ada juga anggota.size()yang mengembalikan jumlah elemen — tidak perlu lagisizeofdibagisizeof. - Baris 11-13 (
data[0]):operator[]berperilaku persis seperti kurung siku array C-style — akses tercepat, tanpa pemeriksaan batas. Indeks di luar batas melalui jalur ini tetap undefined behavior. - Baris 16-23 (
.at()): anggota.at(i)melakukan akses yang sama tetapi memeriksa batas terlebih dahulu. Bila indeks melampaui.size(), ia melempar exception — mekanisme laporan kesalahan runtime yang dibedah tuntas pada Section Error Handling. Di sini kita menangkapnya dengantry/catchagar program tetap berjalan dan bisa mencetak pesan dari.what(). - Baris 26 (
data.fill(7);): mengisi seluruh elemen dengan satu nilai — pemasangan7ke lima slot sekaligus. Fungsionalitas semacam ini tidak dimiliki array C-style. - Baris 33-34 (
lain = data;): penugasan antarstd::arrayyang sama tipenya menyalin seluruh elemen, bukan sekadar alamat. Array C-style bahkan tidak bisa ditulisa = b;sama sekali. - Baris 35-36 (
.front(),.back()): pembaca ringkas untuk elemen pertama dan terakhir —front()setaradata[0],back()setaradata[data.size() - 1].
operator[] vs .at(): Kecepatan atau Pemeriksaan #
std::array menawarkan dua pintu akses yang sama-sama sah, dengan pertimbangan berbeda:
| Aspek | data[i] | data.at(i) |
|---|---|---|
| Pemeriksaan batas | Tidak ada | Ada (melempar std::out_of_range) |
| Biaya saat indeks salah | Undefined behavior | Berhenti dengan pesan yang jelas |
| Kecepatan pada jalur normal | Paling cepat | Cepat, ditambah satu perbandingan |
| Cocok untuk | Loop yang batasnya sudah benar | Indeks dari sumber luar (input, berkas, hasil perhitungan) |
Praktik yang sehat: gunakan operator[] di dalam loop yang batasnya kita kendalikan penuh, dan .at() saat indeks berasal dari luar kendali program. Dengan begitu kesalahan sumber data berubah menjadi pesan yang jelas, bukan perilaku tak terdefinisi yang senyap.
Perbandingan dengan Array C-Style #
Tabel berikut merangkum mengapa std::array menjadi pilihan utama untuk ukuran tetap:
| Aspek | Array C-style int a[5] | std::array<int, 5> |
|---|---|---|
| Mengetahui ukuran sendiri | Tidak (harus sizeof manual, hanya di tempat deklarasi) | Ya, .size() bekerja di mana pun |
| Menjadi parameter fungsi | Meluruh menjadi pointer (ukuran hilang) | Ukuran melekat pada tipe, tetap terbawa |
Penyalinan b = a; | Tidak bisa ditulis langsung | Bisa; menyalin seluruh elemen |
| Dikembalikan dari fungsi | Tidak bisa secara langsung | Bisa, sebagai nilai kembalian biasa |
Akses berbatas .at() | Tidak ada | Ada |
| Antar-muka pustaka standar | Terbatas | Kompatibel penuh (dapat diiterasi, dipakai algoritma) |
| Tata letak memori dan kecepatan | Contiguous, tercepat | Identik — membungkus array C-style |
Poin terakhir itu penting untuk tenang: std::array bukan “array lambat versi aman”. Ia hanyalah pembungkus tipis; setelah kompilasi, kode aksesnya dibangkitkan sama persis dengan array C-style. Kita kehilangan tidak apa-apa, kecuali dua hal kecil: ukuran harus tetap konstanta waktu kompilasi (sama seperti array C-style), dan sintaksnya sedikit lebih panjang.
Dengan std::array menutup kasus ukuran tetap, sisa section ini berpindah ke dunia teks. Chapter berikutnya membahas C-style string — array char dengan aturan khusus — sebelum std::string mengambil alih dengan cara yang jauh lebih nyaman.
Rujukan resmi: cppreference — std::array , cppreference — std::array::at .