Topik ini membahas std::vector, container atau wadah pustaka standar yang paling sering dipakai untuk kumpulan elemen yang jumlahnya berubah saat program berjalan. Pembahasannya mencakup penambahan elemen dengan push_back, perbedaan antara size() dan capacity(), serta fungsi reserve() untuk menyiapkan ruang lebih dahulu. Setelah membaca topik ini, cara kerja alokasi ulang memori dan kebiasaan penggunaan std::vector yang efisien sudah dapat dijelaskan.
Deklarasi dan penambahan elemen#
std::vector berasal dari header <vector> dan menyimpan elemen bertipe sama dalam memori yang berurutan, seperti array, tetapi ukurannya dapat bertambah dan berkurang. Vector kosong dideklarasikan tanpa isi, kemudian diisi satu elemen demi satu elemen dengan push_back(), yang menambahkan elemen baru tepat setelah elemen terakhir. Ukuran elemen saat ini dibaca dengan size(), sedangkan pop_back() menghapus elemen terakhirnya.
#include <iostream>
#include <string>
#include <vector>
int main() {
std::vector<std::string> belanja;
belanja.push_back("beras");
belanja.push_back("telur");
belanja.push_back("minyak");
belanja.push_back("gula");
std::cout << "Isi daftar (" << belanja.size() << " butir):\n";
for (const std::string& b : belanja) {
std::cout << "- " << b << '\n';
}
belanja.pop_back();
std::cout << "Setelah pop_back: " << belanja.size() << " butir\n";
std::cout << "Butir pertama: " << belanja.front() << '\n';
std::cout << "Butir terakhir: " << belanja.back() << '\n';
return 0;
}Isi daftar (4 butir):
- beras
- telur
- minyak
- gula
Setelah pop_back: 3 butir
Butir pertama: beras
Butir terakhir: minyakApabila isinya sudah diketahui sejak awal, vector juga dapat dideklarasikan dengan daftar inisialisasi, misalnya std::vector<int> nilai = {70, 85, 90};, tanpa push_back() sama sekali. Member function front() dan back() membaca elemen pertama dan terakhir tanpa menyalin vectornya, sedangkan akses per indeks memakai belanja[i] seperti array biasa. Kombinasi inilah yang menjadikan vector pilihan awal untuk kumpulan data pada hampir semua program C++ modern.
Vector juga dapat dibuat dengan ukuran awal tertentu, misalnya std::vector<int> penghitung(5); yang berisi lima elemen bernilai 0, atau dengan pasangan ukuran dan nilai awal seperti std::vector<int> penghitung(5, 1); yang berisi lima elemen bernilai 1. Bentuk ini berguna ketika banyaknya kotak sudah diketahui, misalnya penghitung untuk lima kategori nilai. Setelah terbentuk, elemennya tetap dapat ditambah dengan push_back() seperti biasa.
size dan capacity#
size() menyatakan banyaknya elemen yang benar-benar tersimpan, sedangkan capacity() menyatakan jumlah memori yang sudah dialokasikan untuk vector tersebut. Ketika ruang yang dialokasikan habis dan push_back() tetap dipanggil, vector melakukan realokasi, yaitu menyediakan blok memori yang lebih besar, memindahkan seluruh elemen ke sana, lalu melepaskan blok lama. Realokasi inilah yang membuat capacity() biasanya lebih besar daripada size().
#include <iostream>
#include <vector>
int main() {
std::vector<int> angka;
for (int i = 1; i <= 10; ++i) {
angka.push_back(i);
std::cout << "push_back ke-" << i
<< ": size = " << angka.size()
<< ", capacity = " << angka.capacity() << '\n';
}
return 0;
}push_back ke-1: size = 1, capacity = 1
push_back ke-2: size = 2, capacity = 2
push_back ke-3: size = 3, capacity = 4
push_back ke-4: size = 4, capacity = 4
push_back ke-5: size = 5, capacity = 8
push_back ke-6: size = 6, capacity = 8
push_back ke-7: size = 7, capacity = 8
push_back ke-8: size = 8, capacity = 8
push_back ke-9: size = 9, capacity = 16
push_back ke-10: size = 10, capacity = 16Keluaran tersebut memperlihatkan lonjakan capacity pada elemen ke-3, ke-5, dan ke-9, yaitu tepat ketika ruang sebelumnya habis. Nilai size tetap mengikuti banyaknya elemen yang dimasukkan, sedangkan capacity bergerak dalam langkah besar agar push_back() berikutnya tidak langsung memicu realokasi. Dengan demikian, vector menukar sedikit memori berlebih dengan lebih sedikit pemindahan elemen.
Realokasi juga memiliki konsekuensi yang melampaui biaya pemindahan. Referensi, pointer, dan iterator yang menunjuk ke elemen vector menjadi tidak sah setelah realokasi, karena seluruh elemen pindah ke blok memori baru. Karena itu, perulangan yang sedang memegang iterator atau referensi ke elemen vector tidak boleh menambah elemen selama berlangsungnya, sebagaimana ditegaskan pada topik perulangan dan topik iterator.
reserve dan biaya realokasi#
Member function reserve() menyatakan kapasitas minimum yang perlu disiapkan tanpa menambahkan satu elemen pun, sehingga size() tidak berubah. Apabila banyaknya elemen akhir sudah diketahui, satu pemanggilan reserve() menggantikan beberapa realokasi yang terjadi saat pengisian berlangsung. Setiap realokasi yang terhindar berarti satu pemindahan seluruh elemen yang tidak perlu terjadi.
#include <iostream>
#include <vector>
int main() {
std::vector<int> tanpaReserve;
for (int i = 1; i <= 10; ++i) {
tanpaReserve.push_back(i);
}
std::cout << "Tanpa reserve : size = " << tanpaReserve.size()
<< ", capacity = " << tanpaReserve.capacity() << '\n';
std::vector<int> denganReserve;
denganReserve.reserve(10);
for (int i = 1; i <= 10; ++i) {
denganReserve.push_back(i);
}
std::cout << "Dengan reserve: size = " << denganReserve.size()
<< ", capacity = " << denganReserve.capacity() << '\n';
return 0;
}Tanpa reserve : size = 10, capacity = 16
Dengan reserve: size = 10, capacity = 10Kedua vector berisi sepuluh elemen yang sama, tetapi vector yang di-reserve() tidak menyiapkan ruang lebih dari yang diperlukan dan tidak pernah melakukan realokasi selama pengisian. Perbedaan ini terasa pada elemen bertipe besar, misalnya std::string, karena setiap realokasi menyalin seluruh isinya. Untuk elemen int berjumlah sepuluh, selisihnya tidak terasa, tetapi kebiasaan menulisnya sejak awal tetap berguna.
reserve() tidak boleh disamakan dengan resize(). Fungsi resize(n) benar-benar mengubah size(), yaitu menambah elemen bernilai nol atau menghapus elemen yang berlebih, sedangkan reserve(n) hanya menyiapkan kapasitas. Kesalahan paling umum adalah berharap reserve() membuat elemen bisa diakses lewat indeks, padahal elemen tersebut belum ada.
Kapasitas yang sudah membesar tidak menyusut dengan sendirinya. pop_back() dan clear() menurunkan size(), sedangkan capacity() tetap pada nilai terakhirnya, sehingga memori tidak langsung dilepaskan. Permintaan pengembalian memori ke sistem dapat diajukan melalui shrink_to_fit(), dan pustaka bebas memenuhi maupun menolak permintaan tersebut.
Kesalahan umum#
Tiga kesalahan berikut paling sering muncul pada pemakaian std::vector yang baru dipelajari.
- Mengakses
angka[10]pada vector berisi empat elemen. Operator[]tidak memeriksa batas, sehingga aksesnya berujung pada undefined behavior. Gunakanat(), yang melemparstd::out_of_rangedan dapat ditangkap seperti pada topikstd::array, ketika pemeriksaan batas memang diperlukan. - Mencampuradukkan
reserve()danresize().reserve()hanya mengubah kapasitas, sedangkanresize()mengubah ukuran dan mengisi elemennya. Tanyakan tujuannya lebih dahulu, yaitu menyiapkan ruang atau menambah elemen, kemudian pilih fungsinya. - Berharap capacity menyusut setelah
pop_back()atau penghapusan. Kapasitas tetap utuh agar penambahan berikutnya tidak perlu realokasi, sehingga pengurangan memori memerlukan permintaan eksplisitshrink_to_fit()yang boleh ditolak pustaka.
Kesalahan-kesalahan tersebut jarang menghasilkan error yang jelas, sehingga pencegahannya lebih bergantung pada kebiasaan daripada pesan compiler. Menanyakan tiga hal secara teratur sudah cukup, yaitu berapa banyak elemen yang tersimpan, berapa ruang yang sudah disiapkan, dan apakah akses indeksnya berada di dalam batas. Ketiganya dapat dijawab dengan size(), capacity(), dan at() tanpa menambah biaya berarti.
Ringkasan#
std::vectormenyimpan elemen berurutan dengan ukuran yang berubah, danpush_back()besertapop_back()menjadi operasi tambah dan hapus di ujungnya.size()menyatakan banyaknya elemen, sedangkancapacity()menyatakan ruang yang sudah dialokasikan, dan realokasi terjadi ketika ruang tersebut habis.reserve()menyiapkan kapasitas tanpa menambah elemen, sehingga pengisian dengan jumlah yang sudah diketahui tidak memicu realokasi berulang.
Referensi#
- std::vector — cppreference.com — Dokumentasi lengkap std::vector beserta member function-nya.
- std::vector::capacity — cppreference.com — Makna kapasitas dan hubungannya dengan ukuran.
- std::vector::reserve — cppreference.com — Perilaku reserve dan batas jaminannya.
- std::vector::push_back — cppreference.com — Semantik penambahan elemen di akhir vector.
- Introduction to std::vector and list constructors — LearnCpp — Pengantar std::vector beserta inisialisasi dengan daftar nilai.