std::array dan std::vector sudah menjadi sahabat kita sejak Section Array dan String: yang pertama membungkus array C-style dengan ukuran tetap, yang kedua adalah array dinamis yang tumbuh sendiri. Kali ini kita tidak mengulang cara mendeklarasikannya, melainkan menelusuri operasi lanjutan yang membuat keduanya betul-betul berguna — fill, swap, insert, erase, serta pasangan push_back/emplace_back yang sering dipertukarkan tanpa sadar. Memahami mekanisme di balik operasi-operasi ini memudahkan kita menebak biayanya dan memilih container yang tepat.
Sketsa Ulang: Dua Karakter yang Berbeda #
Karakter dasarnya bisa dirangkum dalam satu tabel supaya seluruh pembahasan di bawah punya pijakan.
| Aspek | std::array | std::vector |
|---|---|---|
| Ukuran | tetap saat kompilasi | dinamis saat runtime |
| Penyimpanan | di dalam objek (stack) | buffer di heap |
| Menambah elemen | tidak bisa | push_back, emplace_back, insert |
| Ukuran tipe | sizeof mencakup semua elemen | sizeof tetap kecil, data di luar |
| Cocok untuk | data yang jumlahnya pasti | data yang jumlahnya berubah |
Aturan praktisnya sederhana: bila jumlah elemen pasti dan tidak berubah — misalnya tiga sumbu koordinat — std::array memberi tipe yang paling ringan. Begitu ada pertanyaan “berapa banyak, tergantung input”, jawabannya std::vector.
Operasi yang Sering Dipakai #
Program berikut memperagakan fill pada array, lalu siklus hidup lengkap sebuah vector: menambah, menyisipkan, menghapus, dan menukar isi.
#include <array> // std::array: container ukuran tetap
#include <iostream> // std::cout
#include <string> // std::string
#include <vector> // std::vector: container dinamis
// struct kecil agar push_back vs emplace_back bisa dibedakan
struct Produk {
std::string nama;
int harga;
};
void cetak(const std::vector<std::string>& v, const std::string& label) {
std::cout << label << ":";
for (const std::string& s : v) std::cout << ' ' << s;
std::cout << "\n";
}
int main() {
// --- std::array: ukuran tetap, dikenal saat kompilasi ---
std::array<int, 5> sensor;
sensor.fill(0); // isi seluruh elemen dengan nilai sama
sensor[3] = 42; // akses langsung seperti array biasa
std::cout << "array fill :";
for (int x : sensor) std::cout << ' ' << x;
std::cout << "\n";
// --- std::vector: ukuran tumbuh sesuai kebutuhan ---
std::vector<std::string> keranjang;
keranjang.push_back("Apel"); // menerima string: dibangun lalu dipindah masuk
keranjang.emplace_back("Jeruk"); // bangun string langsung di dalam vector
keranjang.insert(keranjang.begin() + 1, "Mangga");
cetak(keranjang, "keranjang ");
keranjang.erase(keranjang.begin() + 2); // hapus elemen ketiga ("Jeruk")
cetak(keranjang, "setelah erase");
std::cout << "size: " << keranjang.size()
<< ", front: " << keranjang.front()
<< ", back: " << keranjang.back() << "\n";
// --- push_back vs emplace_back pada struct ---
std::vector<Produk> toko;
toko.push_back({"Kopi", 15000}); // buat Produk sementara, lalu pindahkan
toko.emplace_back("Teh", 8000); // bangun Produk langsung di slot akhir
std::cout << "toko: " << toko[0].nama << " " << toko[0].harga
<< ", " << toko[1].nama << " " << toko[1].harga << "\n";
// --- swap: tukar isi dua vector dalam satu langkah ---
std::vector<int> a = {1, 2, 3};
std::vector<int> b = {9, 8, 7, 6};
a.swap(b);
std::cout << "a berisi:";
for (int x : a) std::cout << ' ' << x;
std::cout << "\n";
std::cout << "b berisi:";
for (int x : b) std::cout << ' ' << x;
std::cout << "\n";
return 0;
}Output yang Dihasilkan:
array fill : 0 0 0 42 0
keranjang : Apel Mangga Jeruk
setelah erase: Apel Mangga
size: 2, front: Apel, back: Mangga
toko: Kopi 15000, Teh 8000
a berisi: 9 8 7 6
b berisi: 1 2 3Mari kita bedah:
- Baris 20-21 (
sensor.fill(0)):fillmengisi seluruh elemen array dengan satu nilai — cara aman “menolkan” buffer sebelum dipakai, tanpa loop manual. - Baris 29-31 (menambah elemen):
push_backmenaruh nilai di ujung;insertmenyisipkan di posisi iterator tertentu, sehingga"Mangga"muncul sebagai elemen kedua. Menyisipkan di tengah menggeser semua elemen setelahnya — biaya yang tumbuh seiring panjang vector. - Baris 34 (
erase): menghapus elemen ketiga; elemen-elemen setelahnya digeser maju satu slot dansizeberkurang. Perhatikanerasemenerima iterator, bukan indeks. - Baris 37-39: setelah semua operasi, tersisa
Apel Mangga;front()danback()memberi elemen paling depan dan paling belakang tanpa kita menghitung indeks. - Baris 43-44 (dua gaya menambah Produk): keduanya menghasilkan vector yang sama, tetapi jalurnya berbeda — ini yang kita bedah di bagian berikutnya.
- Baris 51 (
a.swap(b)): isi kedua vector tertukar total. Yang ditukar hanyalah tiga penunjuk internal (buffer, ukuran, kapasitas), bukan elemen satu per satu, sehingga biayanya konstan berapa pun panjangnya.
push_back vs emplace_back #
Perbedaan keduanya ada pada di mana objek dibangun. toko.push_back({"Kopi", 15000}) membangun objek Produk sementara di luar vector, lalu memindahkan objek itu ke slot penyimpanan di ujung vector. Sementara itu toko.emplace_back("Teh", 8000) meneruskan argumen langsung ke constructor Produk yang bekerja di slot akhir vector — tidak ada objek sementara sama sekali. Untuk struct sederhana seperti Produk, selisihnya nyaris tak terukur; untuk tipe yang konstruksinya mahal (misalnya std::string panjang), emplace_back menghemat satu langkah pindah.
Kapan array, Kapan vector #
Kita sekarang punya keputusan yang cukup untuk sebagian besar kasus. Pilih std::array bila ukuran adalah bagian dari tipe datanya: koordinat 3D, matriks kecil, buffer tetap — keuntungannya tidak ada alokasi heap dan compiler tahu ukurannya saat kompilasi. Pilih std::vector untuk semua koleksi lain: hasil parsing file, daftar produk yang bertambah, buffer yang ukurannya dihitung saat runtime. Operasi di tengah (insert, erase) tetap tergolong mahal pada keduanya; bila program kita sangat sering menyisip dan menghapus di tengah atau di kedua ujung, Chapter berikutnya menyiapkan kandidat yang lebih cocok: std::deque dan std::list.
Rujukan resmi: cppreference — std::array , cppreference — std::vector , cppreference — vector::emplace_back .